Merge lp:~phablet-team/messaging-app/side_panel into lp:messaging-app
- side_panel
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~phablet-team/messaging-app/side_panel |
Merge into: | lp:messaging-app |
Prerequisite: | lp:~phablet-team/messaging-app/fix_history_reloading |
Diff against target: |
2988 lines (+1164/-1023) 34 files modified
CMakeLists.txt (+14/-18) TODO.convergence (+3/-0) config.h.in (+1/-0) debian/control (+5/-1) debian/messaging-app-apparmor.additions (+86/-0) debian/messaging-app-apparmor.manifest (+37/-0) debian/messaging-app.install (+1/-0) debian/rules (+22/-0) src/fileoperations.cpp (+6/-1) src/messagingapplication.cpp (+1/-0) src/qml/AudioRecordingBar.qml (+28/-4) src/qml/ComposeBar.qml (+5/-0) src/qml/Dialogs/NoMicrophonePermission.qml (+58/-0) src/qml/EmptyState.qml (+38/-0) src/qml/InputInfo.qml (+24/-0) src/qml/LocalPageWithBottomEdge.qml (+0/-428) src/qml/MMS/MMSAudio.qml (+10/-11) src/qml/MMS/Previewer.qml (+26/-15) src/qml/MMS/PreviewerImage.qml (+1/-0) src/qml/MMS/PreviewerMultipleContacts.qml (+1/-1) src/qml/MMS/PreviewerVideo.qml (+1/-0) src/qml/MMSDelegate.qml (+1/-1) src/qml/MainPage.qml (+123/-64) src/qml/Messages.qml (+418/-350) src/qml/MessagesHeader.qml (+7/-2) src/qml/MessagingBottomEdge.qml (+43/-0) src/qml/MessagingContactEditorPage.qml (+6/-2) src/qml/MessagingContactViewPage.qml (+7/-6) src/qml/MultiRecipientInput.qml (+1/-1) src/qml/NewRecipientPage.qml (+58/-35) src/qml/messaging-app.qml (+109/-30) tests/autopilot/messaging_app/emulators.py (+12/-48) tests/qml/CMakeLists.txt (+2/-1) tests/qml/tst_MessagesView.qml (+9/-4) |
To merge this branch: | bzr merge lp:~phablet-team/messaging-app/side_panel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+282583@code.launchpad.net |
This proposal supersedes a proposal from 2015-11-03.
This proposal has been superseded by a proposal from 2016-01-28.
Commit message
Implement convergent layout for messaging-app.
Description of the change
Implement convergent layout for messaging-app.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:451
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:452
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:454
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:455
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:456
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 457. By Gustavo Pichorim Boiko
-
Make sure we always have a view on the right side of the screen so that we can
add a bottom edge gesture there.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:457
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 458. By Gustavo Pichorim Boiko
-
Use the bottom edge from SDK.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:458
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 459. By Gustavo Pichorim Boiko
-
Make it possible to create the bottom edge page using different properties.
- 460. By Gustavo Pichorim Boiko
-
Add a cancel action to the new message state to be used in the bottom edge pages.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:460
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 461. By Gustavo Pichorim Boiko
-
Fix exposing the bottom edge page with arguments.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:461
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 462. By Gustavo Pichorim Boiko
-
Merge trunk.
- 463. By Gustavo Pichorim Boiko
-
Fix qml tests.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:462
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:463
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 464. By Gustavo Pichorim Boiko
-
Increase the waiting
- 465. By Gustavo Pichorim Boiko
-
Increase the waiting time even more.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:464
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:465
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 466. By Gustavo Pichorim Boiko
-
Move the bottom edge action to the header on dual panel environments
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:466
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 467. By Gustavo Pichorim Boiko
-
Skip the header height from the contacts popup.
- 468. By Gustavo Pichorim Boiko
-
Remove the dependency on unity8-private
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:468
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 469. By Gustavo Pichorim Boiko
-
Remove debug leftover.
- 470. By Gustavo Pichorim Boiko
-
Show a new item on listview when the bottom edge page is visible.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:469
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:470
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 471. By Gustavo Pichorim Boiko
-
Another attempt at scrolling the list to the new item.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:471
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 472. By Gustavo Pichorim Boiko
-
Make sure we get the view on the test before proceeding.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:472
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 473. By Gustavo Pichorim Boiko
-
Fix one more page header.
- 474. By Gustavo Pichorim Boiko
-
Fix one more pagestack interaction
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:474
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 475. By Gustavo Pichorim Boiko
-
Fix sending audio files.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:475
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 476. By Gustavo Pichorim Boiko
-
Start fixing the interaction with header actions and the bottom edge in autopilot
tests.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:476
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 477. By Gustavo Pichorim Boiko
-
Make pep8 happy.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:477
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 478. By Gustavo Pichorim Boiko
-
Fix loading the contact viewing page.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:478
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 479. By Gustavo Pichorim Boiko
-
Make sure the To: field gets focus when composing a new message.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:479
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 480. By Gustavo Pichorim Boiko
-
Pages loaded in the bottom edge are not in the layout, so we need to use the
underneath page as the parent to push new pages to the stack.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:480
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 481. By Gustavo Pichorim Boiko
-
Keep the bottom edge down arrow (back action) visible after sending the first
message.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:481
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 482. By Tiago Salem Herrmann
-
merge apparmor branch
- 483. By Tiago Salem Herrmann
-
Fix ListView anchors to avoid hide the content under the header
- 484. By Gustavo Pichorim Boiko
-
Set the title correctly.
- 485. By Gustavo Pichorim Boiko
-
Make it possible to unselect all too in the main view.
- 486. By Gustavo Pichorim Boiko
-
Fix the header in the messages view.
- 487. By Gustavo Pichorim Boiko
-
Clear some warnings.
- 488. By Gustavo Pichorim Boiko
-
Make sure the page has a height when loading.
- 489. By Tiago Salem Herrmann
-
Fix ListView anchors also on NewRecipientPage
- 490. By Gustavo Pichorim Boiko
-
Create pages manually before pushing to the AdaptivePageLayout as it is very
slow to create the pages by itself. - 491. By Gustavo Pichorim Boiko
-
Make sure pages get destroyed when hitting back
- 492. By Tiago Salem Herrmann
-
Implement back button to destroy dynamically created pages
- 493. By Tiago Salem Herrmann
-
Push all views synchronously
- 494. By Gustavo Pichorim Boiko
-
Make it possible to launch the new message view from settings.
- 495. By Tiago Salem Herrmann
-
fix anchors in the settings page
- 496. By Tiago Salem Herrmann
-
Empty stack before adding SettingsPage to the stack
- 497. By Tiago Salem Herrmann
-
Only set properties once during object creation
implement removePage() that destroy instances when needed - 498. By Tiago Salem Herrmann
-
check before destroying instance
- 499. By Tiago Salem Herrmann
-
anchor previewer to header
- 500. By Gustavo Pichorim Boiko
-
Move the custom page layout functions to a separate QML file.
- 501. By Gustavo Pichorim Boiko
-
Merge swipe to cancel fix.
- 502. By Gustavo Pichorim Boiko
-
Remove duplicated code.
- 503. By Gustavo Pichorim Boiko
-
Fix loading pages by source
- 504. By Gustavo Pichorim Boiko
-
Hide the bottom edge bar in the empty state screen.
- 505. By Gustavo Pichorim Boiko
-
Messaging-app doesn't belong to side stage anymore
- 506. By Gustavo Pichorim Boiko
-
Set a minimum window size.
- 507. By Gustavo Pichorim Boiko
-
Make it landscape by default on windowed environments
- 508. By Gustavo Pichorim Boiko
-
Hide the new message list item once the message is sent.
- 509. By Gustavo Pichorim Boiko
-
Show the group chat participants popup anchored to the right
- 510. By Gustavo Pichorim Boiko
-
Make the header fixed in two column mode
- 511. By Gustavo Pichorim Boiko
-
Add a scrollbar to the Messages view.
- 512. By Gustavo Pichorim Boiko
-
Merge trunk.
- 513. By Gustavo Pichorim Boiko
-
Make the readme more clear
- 514. By Gustavo Pichorim Boiko
-
In dual panel mode, always hide the empty state label.
Unmerged revisions
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-10-13 15:13:32 +0000 |
3 | +++ CMakeLists.txt 2016-01-28 15:00:52 +0000 |
4 | @@ -35,12 +35,7 @@ |
5 | # Instruct CMake to run moc automatically when needed. |
6 | set(CMAKE_AUTOMOC ON) |
7 | |
8 | -configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) |
9 | - |
10 | -#find_package(Qt5Contacts) |
11 | find_package(Qt5DBus) |
12 | -#find_package(Qt5Gui) |
13 | -#find_package(Qt5Multimedia) |
14 | find_package(Qt5Qml) |
15 | find_package(Qt5Quick) |
16 | find_package(Qt5Test) |
17 | @@ -49,23 +44,24 @@ |
18 | include(qt5) |
19 | |
20 | find_package(PkgConfig REQUIRED) |
21 | -#pkg_check_modules(TP_QT5 REQUIRED TelepathyQt5) |
22 | -#pkg_check_modules(TPL_QT5 REQUIRED TelepathyLoggerQt5) |
23 | -#pkg_check_modules(QTGLIB REQUIRED QtGLib-2.0) |
24 | -#pkg_check_modules(GLIB REQUIRED glib-2.0) |
25 | pkg_check_modules(NOTIFY REQUIRED libnotify) |
26 | -#pkg_check_modules(MESSAGING_MENU REQUIRED messaging-menu) |
27 | - |
28 | -# Check if the messaging menu has the message header |
29 | -#set(CMAKE_REQUIRED_INCLUDES ${MESSAGING_MENU_INCLUDE_DIRS}) |
30 | -#check_include_file("messaging-menu-message.h" HAVE_MESSAGING_MENU_MESSAGE) |
31 | - |
32 | -if (HAVE_MESSAGING_MENU_MESSAGE) |
33 | - add_definitions(-DHAVE_MESSAGING_MENU_MESSAGE) |
34 | -endif (HAVE_MESSAGING_MENU_MESSAGE) |
35 | + |
36 | +#find unity8 qml libraries |
37 | +set(UNITY8_QML_PATH /usr/lib/${CMAKE_C_LIBRARY_ARCHITECTURE}/unity8/qml/) |
38 | +find_path(LIB_UNITY_QML_EXISTS NAMES libUnity-qml.so |
39 | + HINTS "${UNITY8_QML_PATH}" |
40 | + NO_CMAKE_PATH |
41 | + NO_CMAKE_ENVIRONMENT_PATH |
42 | + NO_SYSTEM_ENVIRONMENT_PATH |
43 | +) |
44 | +if(!LIB_UNITY_QML_EXISTS) |
45 | + MESSAGE(FATAL_ERROR "unity8 private package not-found") |
46 | +endif() |
47 | |
48 | add_definitions(-DQT_NO_KEYWORDS) |
49 | |
50 | +configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) |
51 | + |
52 | include_directories( |
53 | ${CMAKE_CURRENT_BINARY_DIR} |
54 | ${CMAKE_CURRENT_SOURCE_DIR} |
55 | |
56 | === added file 'TODO.convergence' |
57 | --- TODO.convergence 1970-01-01 00:00:00 +0000 |
58 | +++ TODO.convergence 2016-01-28 15:00:52 +0000 |
59 | @@ -0,0 +1,3 @@ |
60 | +- Right now if you call startChat() in the two panel layout, it won't match the |
61 | + thread on the left side, and if you resize it to one panel, the conversation |
62 | + is closed. |
63 | |
64 | === modified file 'config.h.in' |
65 | --- config.h.in 2014-05-28 20:28:55 +0000 |
66 | +++ config.h.in 2016-01-28 15:00:52 +0000 |
67 | @@ -27,6 +27,7 @@ |
68 | #include <QtDBus/QDBusReply> |
69 | |
70 | #define I18N_DIRECTORY "@CMAKE_INSTALL_PREFIX@/share/locale" |
71 | +#define UNITY8_QML_PATH "@UNITY8_QML_PATH@" |
72 | |
73 | inline bool isRunningInstalled() { |
74 | static bool installed = (QCoreApplication::applicationDirPath() == |
75 | |
76 | === modified file 'debian/control' |
77 | --- debian/control 2016-01-28 15:00:52 +0000 |
78 | +++ debian/control 2016-01-28 15:00:52 +0000 |
79 | @@ -2,8 +2,12 @@ |
80 | Section: x11 |
81 | Priority: optional |
82 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
83 | -Build-Depends: cmake, |
84 | +Build-Depends: apparmor, |
85 | + apparmor-easyprof, |
86 | + apparmor-easyprof-ubuntu (>= 1.3.13), |
87 | + cmake, |
88 | debhelper (>= 9), |
89 | + dh-apparmor, |
90 | dh-translations, |
91 | libnotify-dev, |
92 | python-flake8, |
93 | |
94 | === added file 'debian/messaging-app-apparmor.additions' |
95 | --- debian/messaging-app-apparmor.additions 1970-01-01 00:00:00 +0000 |
96 | +++ debian/messaging-app-apparmor.additions 2016-01-28 15:00:52 +0000 |
97 | @@ -0,0 +1,86 @@ |
98 | + @{HOME}/.local/share/history-service/attachments/** r, |
99 | + |
100 | + # Description: Can access the telephony-service and related services |
101 | + # Usage: reserved |
102 | + |
103 | + # grant full access to telephony service handler and indicator |
104 | + dbus (receive, send) |
105 | + bus=session |
106 | + peer=(name=com.canonical.TelephonyServiceHandler,label=unconfined), |
107 | + |
108 | + dbus (receive, send) |
109 | + bus=session |
110 | + peer=(name=com.canonical.TelephonyServiceIndicator,label=unconfined), |
111 | + |
112 | + # make it possible for apps to register a telepathy observer |
113 | + dbus bind |
114 | + bus=session |
115 | + name=org.freedesktop.Telepathy.Client.*, |
116 | + |
117 | + dbus (send) |
118 | + interface="org.freedesktop.Notifications" |
119 | + member="GetServerInformation", |
120 | + |
121 | + dbus (send) |
122 | + interface="org.freedesktop.Notifications" |
123 | + member="Notify", |
124 | + |
125 | + # query greeter status |
126 | + dbus (receive, send) |
127 | + bus=session |
128 | + peer=(name=com.canonical.UnityGreeter,label=unconfined), |
129 | + |
130 | + # access to telepathy-ofono and other telepathy clients/managers |
131 | + dbus (receive, send) |
132 | + bus=session |
133 | + path=/org/freedesktop/Telepathy/**, |
134 | + |
135 | + dbus (send) |
136 | + bus=session |
137 | + path="/ca/desrt/dconf/Writer/user" |
138 | + interface="ca.desrt.dconf.Writer", |
139 | + |
140 | + # used to query ringtone files and other properties |
141 | + dbus (receive, send) |
142 | + bus=system |
143 | + path="/org/freedesktop/Accounts/User[0-9]*" |
144 | + interface=org.freedesktop.DBus.Properties, |
145 | + |
146 | + dbus (receive, send) |
147 | + bus=session |
148 | + path="/org/freedesktop/DBus" |
149 | + interface=org.freedesktop.DBus, |
150 | + |
151 | + # used by libtelephony-service to order/query existing modems |
152 | + dbus (send) |
153 | + bus=system |
154 | + interface="org.ofono.Manager", |
155 | + |
156 | + # used by apps to get sim contacts |
157 | + dbus (send) |
158 | + bus=system |
159 | + interface="org.ofono.SimManager", |
160 | + |
161 | + dbus (send) |
162 | + bus=system |
163 | + interface="org.ofono.Modem", |
164 | + |
165 | + dbus (send, receive) |
166 | + bus=session |
167 | + peer=(name=com.meego.msyncd,label=unconfined), |
168 | + |
169 | + # used by telepathy-qt to guess existing managers and their features |
170 | + /usr/share/telepathy/managers/* r, |
171 | + # read protocol files and assets |
172 | + /usr/share/telephony-service/** r, |
173 | + |
174 | + # Description: Can access urfkill |
175 | + # Usage: common |
176 | + #include <abstractions/nameservice> |
177 | + |
178 | + allow dbus (receive, send) |
179 | + bus=system |
180 | + path=/org/freedesktop/URfkill, |
181 | + allow dbus (receive, send) |
182 | + bus=system |
183 | + peer=(name=org.freedesktop.URfkill), |
184 | |
185 | === added file 'debian/messaging-app-apparmor.manifest' |
186 | --- debian/messaging-app-apparmor.manifest 1970-01-01 00:00:00 +0000 |
187 | +++ debian/messaging-app-apparmor.manifest 2016-01-28 15:00:52 +0000 |
188 | @@ -0,0 +1,37 @@ |
189 | +{ |
190 | + "profiles": { |
191 | + "messaging-app": { |
192 | + "binary": "/usr/bin/messaging-app", |
193 | + "profile_name": "messaging-app", |
194 | + "policy_vendor": "ubuntu", |
195 | + "policy_version": 1.3, |
196 | + "policy_groups": [ |
197 | + "accounts", |
198 | + "audio", |
199 | + "contacts", |
200 | + "content_exchange", |
201 | + "content_exchange_source", |
202 | + "history", |
203 | + "microphone", |
204 | + "video" |
205 | + ], |
206 | + "abstractions": [ |
207 | + "user-tmp" |
208 | + ], |
209 | + "template_variables": { |
210 | + "APP_ID_DBUS": "messaging_2dapp", |
211 | + "APP_PKGNAME_DBUS": "messaging_2dapp", |
212 | + "APP_PKGNAME": "com.ubuntu.messaging-app" |
213 | + }, |
214 | + "read_path": [ |
215 | + "/usr/share/applications/", |
216 | + "/custom/xdg/data/dconf/", |
217 | + "/usr/share/*/assets/", |
218 | + "@{HOME}/.local/share/evolution/addressbook/*/photos/", |
219 | + "@{HOME}/.cache/messaging-app/HubIncoming/**", |
220 | + "@{HOME}/.config/dconf/user", |
221 | + "/usr/share/messaging-app/" |
222 | + ] |
223 | + } |
224 | + } |
225 | +} |
226 | |
227 | === modified file 'debian/messaging-app.install' |
228 | --- debian/messaging-app.install 2016-01-28 15:00:52 +0000 |
229 | +++ debian/messaging-app.install 2016-01-28 15:00:52 +0000 |
230 | @@ -10,3 +10,4 @@ |
231 | usr/share/messaging-app/Dialogs/*.qml |
232 | usr/share/messaging-app/Stickers/*.qml |
233 | usr/bin/*messaging-app* |
234 | +debian/usr.bin.messaging-app etc/apparmor.d |
235 | |
236 | === modified file 'debian/rules' |
237 | --- debian/rules 2014-08-26 21:26:33 +0000 |
238 | +++ debian/rules 2016-01-28 15:00:52 +0000 |
239 | @@ -4,11 +4,33 @@ |
240 | # Uncomment this to turn on verbose mode. |
241 | #export DH_VERBOSE=1 |
242 | |
243 | +export DEB_BUILD_HARDENING=1 |
244 | export DPKG_GENSYMBOLS_CHECK_LEVEL=4 |
245 | |
246 | %: |
247 | dh $@ --parallel --fail-missing --with translations |
248 | |
249 | +apparmor: |
250 | + aa-easyprof -m ./debian/messaging-app-apparmor.manifest --no-verify | \ |
251 | + egrep -v '(# Click packages|CLICK_DIR)' | \ |
252 | + sed 's/@{APP_PKGNAME}_@{APP_APPNAME}_@{APP_VERSION}/@{APP_PKGNAME}/g' | \ |
253 | + sed 's,Apps/@{APP_PKGNAME},Apps/messaging-app,g' | \ |
254 | + sed '/lttng-ust-/c\ \/{,var\/}run\/shm\/lttng-ust-* r,' | \ |
255 | + sed '/dconf.user rw/c\ \/run\/user\/\[0-9\]*\/dconf\/user rw,' \ |
256 | + > ./debian/usr.bin.messaging-app |
257 | + (head -n -2 ./debian/usr.bin.messaging-app; cat ./debian/messaging-app-apparmor.additions; \ |
258 | + echo } ) > ./debian/usr.bin.messaging-app2 |
259 | + mv ./debian/usr.bin.messaging-app2 ./debian/usr.bin.messaging-app |
260 | + apparmor_parser -QTK ./debian/usr.bin.messaging-app |
261 | + |
262 | +override_dh_install: apparmor |
263 | + dh_install --fail-missing |
264 | + |
265 | +override_dh_installdeb: |
266 | + dh_apparmor --profile-name=usr.bin.messaging-app -pmessaging-app |
267 | + dh_installdeb |
268 | + |
269 | + |
270 | override_dh_auto_test: |
271 | flake8 tests/autopilot/messaging_app/ |
272 | cd obj-$(DEB_HOST_GNU_TYPE); ctest -V |
273 | |
274 | === modified file 'src/fileoperations.cpp' |
275 | --- src/fileoperations.cpp 2016-01-28 15:00:52 +0000 |
276 | +++ src/fileoperations.cpp 2016-01-28 15:00:52 +0000 |
277 | @@ -24,6 +24,7 @@ |
278 | #include <QDir> |
279 | #include <QFile> |
280 | #include <QTemporaryFile> |
281 | +#include <QStandardPaths> |
282 | |
283 | FileOperations::FileOperations(QObject *parent) |
284 | : QObject(parent) |
285 | @@ -37,7 +38,11 @@ |
286 | QString FileOperations::getTemporaryFile(const QString &fileExtension) const |
287 | { |
288 | //TODO remove once lp:1420728 is fixed |
289 | - QTemporaryFile tmp(QDir::tempPath() + "/tmpXXXXXX" + fileExtension); |
290 | + QDir dataLocation(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); |
291 | + if (!dataLocation.exists()) { |
292 | + dataLocation.mkpath("."); |
293 | + } |
294 | + QTemporaryFile tmp(dataLocation.path() + "/tmpXXXXXX" + fileExtension); |
295 | tmp.open(); |
296 | return tmp.fileName(); |
297 | } |
298 | |
299 | === modified file 'src/messagingapplication.cpp' |
300 | --- src/messagingapplication.cpp 2016-01-28 15:00:52 +0000 |
301 | +++ src/messagingapplication.cpp 2016-01-28 15:00:52 +0000 |
302 | @@ -165,6 +165,7 @@ |
303 | m_view->rootContext()->setContextProperty("application", this); |
304 | m_view->rootContext()->setContextProperty("i18nDirectory", I18N_DIRECTORY); |
305 | m_view->engine()->setBaseUrl(QUrl::fromLocalFile(messagingAppDirectory())); |
306 | + m_view->engine()->addImportPath(UNITY8_QML_PATH); |
307 | |
308 | // check if there is a contacts backend override |
309 | QString contactsBackend = qgetenv("QTCONTACTS_MANAGER_OVERRIDE"); |
310 | |
311 | === modified file 'src/qml/AudioRecordingBar.qml' |
312 | --- src/qml/AudioRecordingBar.qml 2016-01-28 15:00:52 +0000 |
313 | +++ src/qml/AudioRecordingBar.qml 2016-01-28 15:00:52 +0000 |
314 | @@ -18,6 +18,7 @@ |
315 | |
316 | import QtQuick 2.0 |
317 | import Ubuntu.Components 1.3 |
318 | +import Ubuntu.Components.Popups 1.3 |
319 | import messagingapp.private 0.1 |
320 | import "dateUtils.js" as DateUtils |
321 | |
322 | @@ -26,6 +27,7 @@ |
323 | opacity: audioRecorder.recording ? 1.0 : 0.0 |
324 | Behavior on opacity { UbuntuNumberAnimation {} } |
325 | visible: opacity > 0 |
326 | + property bool handleError: false |
327 | |
328 | property int duration: 0 |
329 | readonly property bool recording: audioRecorder.recording |
330 | @@ -34,6 +36,7 @@ |
331 | signal audioRecorded(var audio) |
332 | |
333 | function startRecording() { |
334 | + handleError = true |
335 | audioRecorder.record() |
336 | } |
337 | |
338 | @@ -44,7 +47,7 @@ |
339 | Loader { |
340 | id: audioRecorder |
341 | readonly property bool ready: status == Loader.Ready |
342 | - readonly property bool recording: ready ? item.recorderState == AudioRecorder.RecordingState : false |
343 | + readonly property bool recording: ready ? item.recorderStatus == AudioRecorder.RecordingStatus : false |
344 | readonly property int duration: ready ? item.duration : 0 |
345 | function record() { |
346 | audioRecorder.active = true |
347 | @@ -62,9 +65,9 @@ |
348 | Component { |
349 | id: audioRecorderComponent |
350 | AudioRecorder { |
351 | - readonly property bool recording: recorderState == AudioRecorder.RecordingState |
352 | + readonly property bool recording: recorderStatus == AudioRecorder.RecordingStatus |
353 | |
354 | - onRecorderStateChanged: { |
355 | + onRecorderStatusChanged: { |
356 | if (recorderState == AudioRecorder.StoppedState && actualLocation != "") { |
357 | var filePath = actualLocation |
358 | |
359 | @@ -82,12 +85,33 @@ |
360 | recordingBar.duration = duration |
361 | } |
362 | } |
363 | - |
364 | + onErrorChanged: { |
365 | + switch(errorCode) { |
366 | + case AudioRecorder.ResourceError: |
367 | + if (handleError) { |
368 | + timer.start() |
369 | + } |
370 | + break |
371 | + default: |
372 | + } |
373 | + } |
374 | codec: "audio/vorbis" |
375 | quality: AudioRecorder.VeryHighQuality |
376 | } |
377 | } |
378 | |
379 | + // WORKAROUND we can't trigger the dialog from the onErrorChanged signal. |
380 | + Timer { |
381 | + id: timer |
382 | + interval: 1 |
383 | + onTriggered: { |
384 | + Qt.inputMethod.hide() |
385 | + messages.focus = false |
386 | + PopupUtils.open(Qt.createComponent("Dialogs/NoMicrophonePermission.qml").createObject(messages)) |
387 | + handleError = false |
388 | + } |
389 | + } |
390 | + |
391 | TransparentButton { |
392 | id: recordingIcon |
393 | objectName: "recordingIcon" |
394 | |
395 | === modified file 'src/qml/ComposeBar.qml' |
396 | --- src/qml/ComposeBar.qml 2016-01-28 15:00:52 +0000 |
397 | +++ src/qml/ComposeBar.qml 2016-01-28 15:00:52 +0000 |
398 | @@ -23,6 +23,7 @@ |
399 | import Ubuntu.Components.Popups 1.3 |
400 | import Ubuntu.Content 0.1 |
401 | import Ubuntu.Telephony 0.1 |
402 | +import messagingapp.private 0.1 |
403 | import "Stickers" |
404 | |
405 | Item { |
406 | @@ -63,6 +64,10 @@ |
407 | } |
408 | |
409 | function addAttachments(transfer) { |
410 | + if (!transfer || !transfer.items) { |
411 | + return |
412 | + } |
413 | + |
414 | for (var i = 0; i < transfer.items.length; i++) { |
415 | if (String(transfer.items[i].text).length > 0) { |
416 | composeBar.text = String(transfer.items[i].text) |
417 | |
418 | === added file 'src/qml/Dialogs/NoMicrophonePermission.qml' |
419 | --- src/qml/Dialogs/NoMicrophonePermission.qml 1970-01-01 00:00:00 +0000 |
420 | +++ src/qml/Dialogs/NoMicrophonePermission.qml 2016-01-28 15:00:52 +0000 |
421 | @@ -0,0 +1,58 @@ |
422 | +/* |
423 | + * Copyright 2016 Canonical Ltd. |
424 | + * |
425 | + * This file is part of messaging-app. |
426 | + * |
427 | + * dialer-app is free software; you can redistribute it and/or modify |
428 | + * it under the terms of the GNU General Public License as published by |
429 | + * the Free Software Foundation; version 3. |
430 | + * |
431 | + * dialer-app is distributed in the hope that it will be useful, |
432 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
433 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
434 | + * GNU General Public License for more details. |
435 | + * |
436 | + * You should have received a copy of the GNU General Public License |
437 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
438 | + */ |
439 | + |
440 | +import QtQuick 2.0 |
441 | +import Ubuntu.Components 1.3 |
442 | +import Ubuntu.Components.Popups 1.3 |
443 | + |
444 | +Component { |
445 | + Dialog { |
446 | + id: dialogue |
447 | + title: i18n.tr("No permission to access microphone") |
448 | + Column { |
449 | + anchors.left: parent.left |
450 | + anchors.right: parent.right |
451 | + spacing: units.gu(2) |
452 | + |
453 | + Label { |
454 | + anchors.left: parent.left |
455 | + anchors.right: parent.right |
456 | + height: paintedHeight |
457 | + verticalAlignment: Text.AlignVCenter |
458 | + text: i18n.tr("Please grant access on <a href=\"system_settings\">System Settings > Security & Privacy</a>.") |
459 | + wrapMode: Text.WordWrap |
460 | + onLinkActivated: { |
461 | + PopupUtils.close(dialogue) |
462 | + Qt.openUrlExternally("settings:///system/security-privacy") |
463 | + } |
464 | + } |
465 | + Row { |
466 | + spacing: units.gu(4) |
467 | + anchors.horizontalCenter: parent.horizontalCenter |
468 | + Button { |
469 | + objectName: "okNoMicrophonePermission" |
470 | + text: i18n.tr("Ok") |
471 | + color: UbuntuColors.orange |
472 | + onClicked: { |
473 | + PopupUtils.close(dialogue) |
474 | + } |
475 | + } |
476 | + } |
477 | + } |
478 | + } |
479 | +} |
480 | |
481 | === added file 'src/qml/EmptyState.qml' |
482 | --- src/qml/EmptyState.qml 1970-01-01 00:00:00 +0000 |
483 | +++ src/qml/EmptyState.qml 2016-01-28 15:00:52 +0000 |
484 | @@ -0,0 +1,38 @@ |
485 | +import QtQuick 2.0 |
486 | +import Ubuntu.Components 1.3 |
487 | + |
488 | +Item { |
489 | + id: emptyStateScreen |
490 | + |
491 | + property alias labelVisible: emptyStateLabel.visible |
492 | + |
493 | + anchors { |
494 | + left: parent.left |
495 | + leftMargin: units.gu(6) |
496 | + right: parent.right |
497 | + rightMargin: units.gu(6) |
498 | + verticalCenter: parent.verticalCenter |
499 | + } |
500 | + height: childrenRect.height |
501 | + Icon { |
502 | + id: emptyStateIcon |
503 | + anchors.top: emptyStateScreen.top |
504 | + anchors.horizontalCenter: parent.horizontalCenter |
505 | + height: units.gu(5) |
506 | + width: height |
507 | + opacity: 0.3 |
508 | + name: "message" |
509 | + } |
510 | + Label { |
511 | + id: emptyStateLabel |
512 | + anchors.top: emptyStateIcon.bottom |
513 | + anchors.topMargin: units.gu(2) |
514 | + anchors.left: parent.left |
515 | + anchors.right: parent.right |
516 | + text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") |
517 | + color: "#5d5d5d" |
518 | + fontSize: "x-large" |
519 | + wrapMode: Text.WordWrap |
520 | + horizontalAlignment: Text.AlignHCenter |
521 | + } |
522 | +} |
523 | |
524 | === added file 'src/qml/InputInfo.qml' |
525 | --- src/qml/InputInfo.qml 1970-01-01 00:00:00 +0000 |
526 | +++ src/qml/InputInfo.qml 2016-01-28 15:00:52 +0000 |
527 | @@ -0,0 +1,24 @@ |
528 | +import QtQuick 2.0 |
529 | +//import Unity.InputInfo 0.1 |
530 | + |
531 | +Item { |
532 | + // FIXME: implement correctly without relying on unity private stuff |
533 | + property bool hasMouse: mainView.dualPanel //miceModel.count > 0 || touchPadModel.count > 0 |
534 | + property bool hasKeyboard: false //keyboardsModel.count > 0 |
535 | + |
536 | + /*InputDeviceModel { |
537 | + id: miceModel |
538 | + deviceFilter: InputInfo.Mouse |
539 | + } |
540 | + |
541 | + InputDeviceModel { |
542 | + id: touchPadModel |
543 | + deviceFilter: InputInfo.TouchPad |
544 | + } |
545 | + |
546 | + InputDeviceModel { |
547 | + id: keyboardsModel |
548 | + deviceFilter: InputInfo.Keyboard |
549 | + }*/ |
550 | +} |
551 | + |
552 | |
553 | === removed file 'src/qml/LocalPageWithBottomEdge.qml' |
554 | --- src/qml/LocalPageWithBottomEdge.qml 2015-09-14 13:51:27 +0000 |
555 | +++ src/qml/LocalPageWithBottomEdge.qml 1970-01-01 00:00:00 +0000 |
556 | @@ -1,428 +0,0 @@ |
557 | -/* |
558 | - * Copyright (C) 2014 Canonical, Ltd. |
559 | - * |
560 | - * This program is free software; you can redistribute it and/or modify |
561 | - * it under the terms of the GNU General Public License as published by |
562 | - * the Free Software Foundation; version 3. |
563 | - * |
564 | - * This program is distributed in the hope that it will be useful, |
565 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
566 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
567 | - * GNU General Public License for more details. |
568 | - * |
569 | - * You should have received a copy of the GNU General Public License |
570 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
571 | - */ |
572 | - |
573 | -/* |
574 | - Example: |
575 | - |
576 | - MainView { |
577 | - objectName: "mainView" |
578 | - |
579 | - applicationName: "com.ubuntu.developer.boiko.bottomedge" |
580 | - |
581 | - width: units.gu(100) |
582 | - height: units.gu(75) |
583 | - |
584 | - Component { |
585 | - id: pageComponent |
586 | - |
587 | - PageWithBottomEdge { |
588 | - id: mainPage |
589 | - title: i18n.tr("Main Page") |
590 | - |
591 | - Rectangle { |
592 | - anchors.fill: parent |
593 | - color: "white" |
594 | - } |
595 | - |
596 | - bottomEdgePageComponent: Page { |
597 | - title: "Contents" |
598 | - anchors.fill: parent |
599 | - //anchors.topMargin: contentsPage.flickable.contentY |
600 | - |
601 | - ListView { |
602 | - anchors.fill: parent |
603 | - model: 50 |
604 | - delegate: ListItems.Standard { |
605 | - text: "One Content Item: " + index |
606 | - } |
607 | - } |
608 | - } |
609 | - bottomEdgeTitle: i18n.tr("Bottom edge action") |
610 | - } |
611 | - } |
612 | - |
613 | - PageStack { |
614 | - id: stack |
615 | - Component.onCompleted: stack.push(pageComponent) |
616 | - } |
617 | - } |
618 | - |
619 | -*/ |
620 | - |
621 | -import QtQuick 2.2 |
622 | -import Ubuntu.Components 1.3 |
623 | - |
624 | -Page { |
625 | - id: page |
626 | - |
627 | - property alias bottomEdgePageComponent: edgeLoader.sourceComponent |
628 | - property alias bottomEdgePageSource: edgeLoader.source |
629 | - property alias bottomEdgeTitle: tipLabel.text |
630 | - property bool bottomEdgeEnabled: true |
631 | - property int bottomEdgeExpandThreshold: page.height * 0.2 |
632 | - property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded |
633 | - property bool reloadBottomEdgePage: true |
634 | - |
635 | - readonly property alias bottomEdgePage: edgeLoader.item |
636 | - readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active) |
637 | - readonly property bool isCollapsed: (bottomEdge.y === page.height) |
638 | - readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready) |
639 | - property var temporaryProperties: null |
640 | - |
641 | - property bool _showEdgePageWhenReady: false |
642 | - property int _areaWhenExpanded: 0 |
643 | - |
644 | - signal bottomEdgeReleased() |
645 | - signal bottomEdgeDismissed() |
646 | - |
647 | - |
648 | - function showBottomEdgePage(source, properties) |
649 | - { |
650 | - edgeLoader.setSource(source, properties) |
651 | - temporaryProperties = properties |
652 | - _showEdgePageWhenReady = true |
653 | - } |
654 | - |
655 | - function setBottomEdgePage(source, properties) |
656 | - { |
657 | - edgeLoader.setSource(source, properties) |
658 | - } |
659 | - |
660 | - function _pushPage() |
661 | - { |
662 | - if (edgeLoader.status === Loader.Ready) { |
663 | - edgeLoader.item.active = true |
664 | - page.pageStack.push(edgeLoader.item) |
665 | - if (edgeLoader.item.flickable) { |
666 | - edgeLoader.item.flickable.contentY = -page.header.height |
667 | - edgeLoader.item.flickable.returnToBounds() |
668 | - } |
669 | - if (edgeLoader.item.ready) |
670 | - edgeLoader.item.ready() |
671 | - } |
672 | - } |
673 | - |
674 | - |
675 | - Component.onCompleted: { |
676 | - // avoid a binding on the expanded height value |
677 | - var expandedHeight = height; |
678 | - _areaWhenExpanded = expandedHeight; |
679 | - } |
680 | - |
681 | - onActiveChanged: { |
682 | - if (active) { |
683 | - bottomEdge.state = "collapsed" |
684 | - } |
685 | - } |
686 | - |
687 | - onBottomEdgePageLoadedChanged: { |
688 | - if (_showEdgePageWhenReady && bottomEdgePageLoaded) { |
689 | - bottomEdge.state = "expanded" |
690 | - _showEdgePageWhenReady = false |
691 | - } |
692 | - } |
693 | - |
694 | - InverseMouseArea { |
695 | - anchors.fill: edgeLoader.item |
696 | - sensingArea: mainView |
697 | - enabled: !tip.hidden |
698 | - onPressed: { |
699 | - mouse.accepted = false |
700 | - page.focus = false |
701 | - } |
702 | - z: 1 |
703 | - } |
704 | - |
705 | - Rectangle { |
706 | - id: bgVisual |
707 | - |
708 | - color: "black" |
709 | - anchors.fill: page |
710 | - opacity: 0.7 * ((page.height - bottomEdge.y) / page.height) |
711 | - z: 1 |
712 | - } |
713 | - |
714 | - UbuntuShape { |
715 | - id: tip |
716 | - objectName: "bottomEdgeTip" |
717 | - |
718 | - property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y) |
719 | - |
720 | - enabled: mouseArea.enabled |
721 | - anchors { |
722 | - bottom: parent.bottom |
723 | - horizontalCenter: bottomEdge.horizontalCenter |
724 | - bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1) |
725 | - Behavior on bottomMargin { |
726 | - SequentialAnimation { |
727 | - // wait some msecs in case of the focus change again, to avoid flickering |
728 | - PauseAnimation { |
729 | - duration: 300 |
730 | - } |
731 | - UbuntuNumberAnimation { |
732 | - duration: UbuntuAnimation.SnapDuration |
733 | - } |
734 | - } |
735 | - } |
736 | - } |
737 | - |
738 | - z: 1 |
739 | - width: tipLabel.paintedWidth + units.gu(6) |
740 | - height: bottomEdge.tipHeight + units.gu(1) |
741 | - color: Theme.palette.normal.overlay |
742 | - Label { |
743 | - id: tipLabel |
744 | - |
745 | - anchors { |
746 | - top: parent.top |
747 | - left: parent.left |
748 | - right: parent.right |
749 | - } |
750 | - height: bottomEdge.tipHeight |
751 | - verticalAlignment: Text.AlignVCenter |
752 | - horizontalAlignment: Text.AlignHCenter |
753 | - opacity: tip.hidden ? 0.0 : 1.0 |
754 | - Behavior on opacity { |
755 | - UbuntuNumberAnimation { |
756 | - duration: UbuntuAnimation.SnapDuration |
757 | - } |
758 | - } |
759 | - } |
760 | - } |
761 | - |
762 | - Rectangle { |
763 | - id: shadow |
764 | - |
765 | - anchors { |
766 | - left: parent.left |
767 | - right: parent.right |
768 | - bottom: parent.bottom |
769 | - } |
770 | - height: units.gu(1) |
771 | - z: 1 |
772 | - opacity: 0.0 |
773 | - gradient: Gradient { |
774 | - GradientStop { position: 0.0; color: "transparent" } |
775 | - GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) } |
776 | - } |
777 | - } |
778 | - |
779 | - MouseArea { |
780 | - id: mouseArea |
781 | - |
782 | - property real previousY: -1 |
783 | - property string dragDirection: "None" |
784 | - |
785 | - preventStealing: true |
786 | - drag { |
787 | - axis: Drag.YAxis |
788 | - target: bottomEdge |
789 | - minimumY: bottomEdge.pageStartY |
790 | - maximumY: page.height |
791 | - } |
792 | - enabled: edgeLoader.status == Loader.Ready |
793 | - |
794 | - anchors { |
795 | - left: parent.left |
796 | - right: parent.right |
797 | - bottom: parent.bottom |
798 | - |
799 | - } |
800 | - height: bottomEdge.tipHeight |
801 | - z: 1 |
802 | - |
803 | - onReleased: { |
804 | - page.bottomEdgeReleased() |
805 | - if ((dragDirection === "BottomToTop") && |
806 | - bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) { |
807 | - bottomEdge.state = "expanded" |
808 | - } else { |
809 | - bottomEdge.state = "collapsed" |
810 | - } |
811 | - previousY = -1 |
812 | - dragDirection = "None" |
813 | - } |
814 | - |
815 | - onPressed: { |
816 | - previousY = mouse.y |
817 | - tip.forceActiveFocus() |
818 | - } |
819 | - |
820 | - onMouseYChanged: { |
821 | - var yOffset = previousY - mouseY |
822 | - // skip if was a small move |
823 | - if (Math.abs(yOffset) <= units.gu(2)) { |
824 | - return |
825 | - } |
826 | - previousY = mouseY |
827 | - dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom" |
828 | - } |
829 | - } |
830 | - |
831 | - Rectangle { |
832 | - id: bottomEdge |
833 | - objectName: "bottomEdge" |
834 | - |
835 | - readonly property int tipHeight: units.gu(3) |
836 | - readonly property int pageStartY: 0 |
837 | - |
838 | - z: 1 |
839 | - color: Theme.palette.normal.background |
840 | - clip: true |
841 | - anchors { |
842 | - left: parent.left |
843 | - right: parent.right |
844 | - } |
845 | - height: page.height |
846 | - y: height |
847 | - visible: page.bottomEdgeEnabled && !page.isCollapsed |
848 | - state: "collapsed" |
849 | - states: [ |
850 | - State { |
851 | - name: "collapsed" |
852 | - PropertyChanges { |
853 | - target: bottomEdge |
854 | - y: bottomEdge.height |
855 | - } |
856 | - }, |
857 | - State { |
858 | - name: "expanded" |
859 | - PropertyChanges { |
860 | - target: bottomEdge |
861 | - y: bottomEdge.pageStartY |
862 | - } |
863 | - }, |
864 | - State { |
865 | - name: "floating" |
866 | - when: mouseArea.drag.active |
867 | - PropertyChanges { |
868 | - target: shadow |
869 | - opacity: 1.0 |
870 | - } |
871 | - } |
872 | - ] |
873 | - |
874 | - transitions: [ |
875 | - Transition { |
876 | - to: "expanded" |
877 | - SequentialAnimation { |
878 | - alwaysRunToEnd: true |
879 | - |
880 | - SmoothedAnimation { |
881 | - target: bottomEdge |
882 | - property: "y" |
883 | - duration: UbuntuAnimation.FastDuration |
884 | - easing.type: Easing.Linear |
885 | - } |
886 | - SmoothedAnimation { |
887 | - target: edgeLoader |
888 | - property: "anchors.topMargin" |
889 | - to: - units.gu(4) |
890 | - duration: UbuntuAnimation.FastDuration |
891 | - easing.type: Easing.Linear |
892 | - } |
893 | - SmoothedAnimation { |
894 | - target: edgeLoader |
895 | - property: "anchors.topMargin" |
896 | - to: 0 |
897 | - duration: UbuntuAnimation.FastDuration |
898 | - easing: UbuntuAnimation.StandardEasing |
899 | - } |
900 | - ScriptAction { |
901 | - script: page._pushPage() |
902 | - } |
903 | - } |
904 | - }, |
905 | - Transition { |
906 | - from: "expanded" |
907 | - to: "collapsed" |
908 | - SequentialAnimation { |
909 | - alwaysRunToEnd: true |
910 | - |
911 | - ScriptAction { |
912 | - script: { |
913 | - Qt.inputMethod.hide() |
914 | - edgeLoader.item.parent = edgeLoader |
915 | - edgeLoader.item.anchors.fill = edgeLoader |
916 | - edgeLoader.item.active = false |
917 | - } |
918 | - } |
919 | - SmoothedAnimation { |
920 | - target: bottomEdge |
921 | - property: "y" |
922 | - duration: UbuntuAnimation.SlowDuration |
923 | - } |
924 | - ScriptAction { |
925 | - script: { |
926 | - // destroy current bottom page |
927 | - if (page.reloadBottomEdgePage) { |
928 | - edgeLoader.active = false |
929 | - // remove properties from old instance |
930 | - if (edgeLoader.source !== "") { |
931 | - var properties = {} |
932 | - if (temporaryProperties !== null) { |
933 | - properties = temporaryProperties |
934 | - temporaryProperties = null |
935 | - } |
936 | - |
937 | - edgeLoader.setSource(edgeLoader.source, properties) |
938 | - } |
939 | - // tip will receive focus on page active true |
940 | - } else { |
941 | - tip.forceActiveFocus() |
942 | - } |
943 | - |
944 | - // notify |
945 | - page.bottomEdgeDismissed() |
946 | - |
947 | - edgeLoader.active = true |
948 | - } |
949 | - } |
950 | - } |
951 | - }, |
952 | - Transition { |
953 | - from: "floating" |
954 | - to: "collapsed" |
955 | - SmoothedAnimation { |
956 | - target: bottomEdge |
957 | - property: "y" |
958 | - duration: UbuntuAnimation.FastDuration |
959 | - } |
960 | - } |
961 | - ] |
962 | - |
963 | - Loader { |
964 | - id: edgeLoader |
965 | - |
966 | - asynchronous: true |
967 | - anchors.fill: parent |
968 | - //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging |
969 | - Binding { |
970 | - target: edgeLoader.status === Loader.Ready ? edgeLoader : null |
971 | - property: "anchors.topMargin" |
972 | - value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0 |
973 | - when: !page.isReady |
974 | - } |
975 | - |
976 | - onLoaded: { |
977 | - tip.forceActiveFocus() |
978 | - if (page.isReady && edgeLoader.item.active !== true) { |
979 | - page._pushPage() |
980 | - } |
981 | - } |
982 | - } |
983 | - } |
984 | -} |
985 | |
986 | === modified file 'src/qml/MMS/MMSAudio.qml' |
987 | --- src/qml/MMS/MMSAudio.qml 2016-01-28 15:00:52 +0000 |
988 | +++ src/qml/MMS/MMSAudio.qml 2016-01-28 15:00:52 +0000 |
989 | @@ -32,15 +32,6 @@ |
990 | property string textColor: incoming ? "#5D5D5D" : "#FFFFFF" |
991 | swipeLocked: audioPlayer.playing |
992 | |
993 | - onAttachmentChanged: { |
994 | - var tmpFile = FileOperations.getTemporaryFile(".ogg") |
995 | - if (FileOperations.link(attachment.filePath, tmpFile)) { |
996 | - audioPlayer.source = tmpFile; |
997 | - } else { |
998 | - console.log("MMSAudio: Failed to link", attachment.filePath, "to", tmpFile) |
999 | - } |
1000 | - } |
1001 | - |
1002 | Rectangle { |
1003 | id: shape |
1004 | radius: units.gu(1) |
1005 | @@ -63,7 +54,15 @@ |
1006 | readonly property int playbackState: ready ? item.playbackState : Audio.StoppedState |
1007 | property bool muted: false |
1008 | property string source: "" |
1009 | - function play() { |
1010 | + function play() { |
1011 | + var tmpFile = FileOperations.getTemporaryFile(".ogg") |
1012 | + if (FileOperations.link(attachment.filePath, tmpFile)) { |
1013 | + source = tmpFile; |
1014 | + } else { |
1015 | + console.log("MMSAudio: Failed to link", attachment.filePath, "to", tmpFile) |
1016 | + return |
1017 | + } |
1018 | + |
1019 | audioPlayer.active = true |
1020 | item.play() |
1021 | } |
1022 | @@ -76,6 +75,7 @@ |
1023 | function seek(pos) { item.seek(pos) } |
1024 | active: false |
1025 | sourceComponent: audioPlayerComponent |
1026 | + Component.onDestruction: FileOperations.remove(source) |
1027 | } |
1028 | |
1029 | Component { |
1030 | @@ -104,7 +104,6 @@ |
1031 | |
1032 | spacing: units.gu(1) |
1033 | sideBySide: true |
1034 | - enabled: audioPlayer.source != "" |
1035 | iconColor: audioDelegate.textColor |
1036 | iconName: audioPlayer.playing ? "media-playback-pause" : "media-playback-start" |
1037 | |
1038 | |
1039 | === modified file 'src/qml/MMS/Previewer.qml' |
1040 | --- src/qml/MMS/Previewer.qml 2015-11-17 14:39:21 +0000 |
1041 | +++ src/qml/MMS/Previewer.qml 2016-01-28 15:00:52 +0000 |
1042 | @@ -31,7 +31,7 @@ |
1043 | |
1044 | function handleAttachment(filePath, handlerType) |
1045 | { |
1046 | - mainStack.push(picker, {"url": filePath, "handler": handlerType}); |
1047 | + mainStack.addPageToCurrentColumn(previewerPage, picker, {"url": filePath, "handler": handlerType}); |
1048 | actionTriggered() |
1049 | } |
1050 | |
1051 | @@ -45,23 +45,29 @@ |
1052 | previewerPage.handleAttachment(attachment.filePath, ContentHandler.Share) |
1053 | } |
1054 | |
1055 | - function backAction() |
1056 | - { |
1057 | - mainStack.pop() |
1058 | + header: PageHeader { |
1059 | + id: pageHeader |
1060 | + |
1061 | + property alias leadingActions: leadingBar.actions |
1062 | + property alias trailingActions: trailingBar.actions |
1063 | + |
1064 | + title: "" |
1065 | + leadingActionBar { |
1066 | + id: leadingBar |
1067 | + } |
1068 | + |
1069 | + trailingActionBar { |
1070 | + id: trailingBar |
1071 | + } |
1072 | } |
1073 | |
1074 | - title: "" |
1075 | state: "default" |
1076 | states: [ |
1077 | - PageHeadState { |
1078 | + State { |
1079 | + id: defaultState |
1080 | name: "default" |
1081 | - head: previewerPage.head |
1082 | - backAction: Action { |
1083 | - iconName: "back" |
1084 | - text: i18n.tr("Back") |
1085 | - onTriggered: previewerPage.backAction() |
1086 | - } |
1087 | - actions: [ |
1088 | + |
1089 | + property list<QtObject> trailingActions: [ |
1090 | Action { |
1091 | objectName: "saveButton" |
1092 | text: i18n.tr("Save") |
1093 | @@ -75,6 +81,11 @@ |
1094 | onTriggered: previewerPage.shareAttchment() |
1095 | } |
1096 | ] |
1097 | + PropertyChanges { |
1098 | + target: pageHeader |
1099 | + |
1100 | + trailingActions: defaultState.trailingActions |
1101 | + } |
1102 | } |
1103 | ] |
1104 | |
1105 | @@ -109,11 +120,11 @@ |
1106 | |
1107 | onPeerSelected: { |
1108 | picker.curTransfer = peer.request(); |
1109 | - mainStack.pop(); |
1110 | + mainStack.removePages(picker); |
1111 | if (picker.curTransfer.state === ContentTransfer.InProgress) |
1112 | picker.__exportItems(picker.url); |
1113 | } |
1114 | - onCancelPressed: mainStack.pop(); |
1115 | + onCancelPressed: mainStack.removePages(picker); |
1116 | } |
1117 | |
1118 | Connections { |
1119 | |
1120 | === modified file 'src/qml/MMS/PreviewerImage.qml' |
1121 | --- src/qml/MMS/PreviewerImage.qml 2016-01-28 15:00:52 +0000 |
1122 | +++ src/qml/MMS/PreviewerImage.qml 2016-01-28 15:00:52 +0000 |
1123 | @@ -25,6 +25,7 @@ |
1124 | Previewer { |
1125 | id: imagePreviewer |
1126 | |
1127 | + // FIXME: this won't work correctly in windowed mode |
1128 | Component.onCompleted: application.fullscreen = true |
1129 | Component.onDestruction: application.fullscreen = false |
1130 | |
1131 | |
1132 | === modified file 'src/qml/MMS/PreviewerMultipleContacts.qml' |
1133 | --- src/qml/MMS/PreviewerMultipleContacts.qml 2015-11-19 00:34:45 +0000 |
1134 | +++ src/qml/MMS/PreviewerMultipleContacts.qml 2016-01-28 15:00:52 +0000 |
1135 | @@ -51,7 +51,7 @@ |
1136 | property var contact: thumbnail.vcard.contacts[index] |
1137 | |
1138 | onClicked: { |
1139 | - mainStack.push(sigleContatPreviewer, {'contact': contact}) |
1140 | + mainStack.addPageToCurrentColumn(root, sigleContatPreviewer, {'contact': contact}) |
1141 | } |
1142 | } |
1143 | } |
1144 | |
1145 | === modified file 'src/qml/MMS/PreviewerVideo.qml' |
1146 | --- src/qml/MMS/PreviewerVideo.qml 2016-01-28 15:00:52 +0000 |
1147 | +++ src/qml/MMS/PreviewerVideo.qml 2016-01-28 15:00:52 +0000 |
1148 | @@ -30,6 +30,7 @@ |
1149 | title: i18n.tr("Video Preview") |
1150 | clip: true |
1151 | |
1152 | + // FIXME: this won't work correctly in windowed mode |
1153 | Component.onCompleted: { |
1154 | application.fullscreen = true |
1155 | // Load Video player after toggling fullscreen to reduce flickering |
1156 | |
1157 | === modified file 'src/qml/MMSDelegate.qml' |
1158 | --- src/qml/MMSDelegate.qml 2016-01-28 15:00:52 +0000 |
1159 | +++ src/qml/MMSDelegate.qml 2016-01-28 15:00:52 +0000 |
1160 | @@ -45,7 +45,7 @@ |
1161 | var properties = {} |
1162 | properties["attachment"] = attachment.item.attachment |
1163 | properties["thumbnail"] = attachment.item |
1164 | - mainStack.push(Qt.resolvedUrl(attachment.item.previewer), properties) |
1165 | + mainStack.addPageToCurrentColumn(messages.basePage, Qt.resolvedUrl(attachment.item.previewer), properties) |
1166 | } |
1167 | } |
1168 | |
1169 | |
1170 | === modified file 'src/qml/MainPage.qml' |
1171 | --- src/qml/MainPage.qml 2016-01-28 15:00:52 +0000 |
1172 | +++ src/qml/MainPage.qml 2016-01-28 15:00:52 +0000 |
1173 | @@ -23,29 +23,25 @@ |
1174 | import Ubuntu.History 0.1 |
1175 | import "dateUtils.js" as DateUtils |
1176 | |
1177 | -LocalPageWithBottomEdge { |
1178 | +Page { |
1179 | id: mainPage |
1180 | property alias selectionMode: threadList.isInSelectionMode |
1181 | property bool searching: false |
1182 | + property bool isEmpty: threadCount == 0 && !threadModel.canFetchMore |
1183 | property alias threadCount: threadList.count |
1184 | + property alias displayedThreadIndex: threadList.currentIndex |
1185 | |
1186 | function startSelection() { |
1187 | threadList.startSelection() |
1188 | } |
1189 | |
1190 | - state: selectionMode ? "select" : searching ? "search" : "default" |
1191 | - title: selectionMode ? " " : i18n.tr("Messages") |
1192 | - flickable: null |
1193 | - |
1194 | - bottomEdgeEnabled: !selectionMode && !searching |
1195 | - bottomEdgeTitle: i18n.tr("+") |
1196 | - bottomEdgePageComponent: Messages { active: false } |
1197 | - |
1198 | TextField { |
1199 | id: searchField |
1200 | objectName: "searchField" |
1201 | visible: mainPage.searching |
1202 | anchors { |
1203 | + top: parent.top |
1204 | + topMargin: units.gu(1) |
1205 | left: parent.left |
1206 | right: parent.right |
1207 | rightMargin: units.gu(2) |
1208 | @@ -60,11 +56,30 @@ |
1209 | } |
1210 | } |
1211 | |
1212 | + header: PageHeader { |
1213 | + id: pageHeader |
1214 | + |
1215 | + property alias leadingActions: leadingBar.actions |
1216 | + property alias trailingActions: trailingBar.actions |
1217 | + |
1218 | + title: i18n.tr("Messages") |
1219 | + flickable: threadList |
1220 | + leadingActionBar { |
1221 | + id: leadingBar |
1222 | + } |
1223 | + |
1224 | + trailingActionBar { |
1225 | + id: trailingBar |
1226 | + } |
1227 | + } |
1228 | + |
1229 | states: [ |
1230 | - PageHeadState { |
1231 | + State { |
1232 | + id: defaultState |
1233 | name: "default" |
1234 | - head: mainPage.head |
1235 | - actions: [ |
1236 | + when: !searching && !selectionMode |
1237 | + |
1238 | + property list<QtObject> trailingActions: [ |
1239 | Action { |
1240 | objectName: "searchAction" |
1241 | iconName: "search" |
1242 | @@ -77,34 +92,63 @@ |
1243 | objectName: "settingsAction" |
1244 | text: i18n.tr("Settings") |
1245 | iconName: "settings" |
1246 | - onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) |
1247 | + onTriggered: pageStack.addPageToNextColumn(mainPage, Qt.resolvedUrl("SettingsPage.qml")) |
1248 | + }, |
1249 | + Action { |
1250 | + objectName: "newMessageAction" |
1251 | + text: i18n.tr("New message") |
1252 | + iconName: "add" |
1253 | + visible: dualPanel |
1254 | + onTriggered: mainView.bottomEdge.commit() |
1255 | } |
1256 | + |
1257 | ] |
1258 | + |
1259 | + PropertyChanges { |
1260 | + target: pageHeader |
1261 | + trailingActions: defaultState.trailingActions |
1262 | + leadingActions: [] |
1263 | + } |
1264 | }, |
1265 | - PageHeadState { |
1266 | + State { |
1267 | + id: searchState |
1268 | name: "search" |
1269 | - head: mainPage.head |
1270 | - backAction: Action { |
1271 | - objectName: "cancelSearch" |
1272 | - visible: mainPage.searching |
1273 | - iconName: "back" |
1274 | - text: i18n.tr("Cancel") |
1275 | - onTriggered: { |
1276 | - searchField.text = "" |
1277 | - mainPage.searching = false |
1278 | + when: searching |
1279 | + |
1280 | + property list<QtObject> leadingActions: [ |
1281 | + Action { |
1282 | + objectName: "cancelSearch" |
1283 | + visible: mainPage.searching |
1284 | + iconName: "back" |
1285 | + text: i18n.tr("Cancel") |
1286 | + onTriggered: { |
1287 | + searchField.text = "" |
1288 | + mainPage.searching = false |
1289 | + } |
1290 | } |
1291 | + ] |
1292 | + |
1293 | + PropertyChanges { |
1294 | + target: pageHeader |
1295 | + contents: searchField |
1296 | + leadingActions: searchState.leadingActions |
1297 | + trailingActions: [] |
1298 | } |
1299 | - contents: searchField |
1300 | }, |
1301 | - PageHeadState { |
1302 | - name: "select" |
1303 | - head: mainPage.head |
1304 | - backAction: Action { |
1305 | - objectName: "selectionModeCancelAction" |
1306 | - iconName: "back" |
1307 | - onTriggered: threadList.cancelSelection() |
1308 | - } |
1309 | - actions: [ |
1310 | + State { |
1311 | + id: selectionState |
1312 | + name: "selection" |
1313 | + when: selectionMode |
1314 | + |
1315 | + property list<QtObject> leadingActions: [ |
1316 | + Action { |
1317 | + objectName: "selectionModeCancelAction" |
1318 | + iconName: "back" |
1319 | + onTriggered: threadList.cancelSelection() |
1320 | + } |
1321 | + ] |
1322 | + |
1323 | + property list<QtObject> trailingActions: [ |
1324 | Action { |
1325 | objectName: "selectionModeSelectAllAction" |
1326 | iconName: "select" |
1327 | @@ -117,39 +161,18 @@ |
1328 | onTriggered: threadList.endSelection() |
1329 | } |
1330 | ] |
1331 | + PropertyChanges { |
1332 | + target: pageHeader |
1333 | + title: " " |
1334 | + leadingActions: selectionState.leadingActions |
1335 | + trailingActions: selectionState.trailingActions |
1336 | + } |
1337 | } |
1338 | ] |
1339 | |
1340 | - Item { |
1341 | + EmptyState { |
1342 | id: emptyStateScreen |
1343 | - anchors.left: parent.left |
1344 | - anchors.leftMargin: units.gu(6) |
1345 | - anchors.right: parent.right |
1346 | - anchors.rightMargin: units.gu(6) |
1347 | - height: childrenRect.height |
1348 | - anchors.verticalCenter: parent.verticalCenter |
1349 | - visible: threadCount == 0 && !threadModel.canFetchMore |
1350 | - Icon { |
1351 | - id: emptyStateIcon |
1352 | - anchors.top: emptyStateScreen.top |
1353 | - anchors.horizontalCenter: parent.horizontalCenter |
1354 | - height: units.gu(5) |
1355 | - width: height |
1356 | - opacity: 0.3 |
1357 | - name: "message" |
1358 | - } |
1359 | - Label { |
1360 | - id: emptyStateLabel |
1361 | - anchors.top: emptyStateIcon.bottom |
1362 | - anchors.topMargin: units.gu(2) |
1363 | - anchors.left: parent.left |
1364 | - anchors.right: parent.right |
1365 | - text: i18n.tr("Compose a new message by swiping up from the bottom of the screen.") |
1366 | - color: "#5d5d5d" |
1367 | - fontSize: "x-large" |
1368 | - wrapMode: Text.WordWrap |
1369 | - horizontalAlignment: Text.AlignHCenter |
1370 | - } |
1371 | + visible: mainPage.isEmpty && !mainView.dualPanel |
1372 | } |
1373 | |
1374 | Component { |
1375 | @@ -188,8 +211,18 @@ |
1376 | clip: true |
1377 | cacheBuffer: threadList.height * 2 |
1378 | section.property: "eventDate" |
1379 | + currentIndex: -1 |
1380 | //spacing: searchField.text === "" ? units.gu(-2) : 0 |
1381 | section.delegate: searching && searchField.text !== "" ? null : sectionDelegate |
1382 | + header: ListItem.Standard { |
1383 | + id: newItem |
1384 | + height: mainView.bottomEdge.status === BottomEdge.Committed ? units.gu(10) : 0 |
1385 | + text: i18n.tr("New message") |
1386 | + iconName: "message-new" |
1387 | + iconFrame: false |
1388 | + selected: true |
1389 | + } |
1390 | + |
1391 | listDelegate: ThreadDelegate { |
1392 | id: threadDelegate |
1393 | // FIXME: find a better unique name |
1394 | @@ -201,7 +234,15 @@ |
1395 | } |
1396 | height: units.gu(8) |
1397 | selectionMode: threadList.isInSelectionMode |
1398 | - selected: threadList.isSelected(threadDelegate) |
1399 | + selected: { |
1400 | + if (selectionMode) { |
1401 | + return threadList.isSelected(threadDelegate) |
1402 | + } else { |
1403 | + // FIXME: there might be a better way of doing this |
1404 | + return index === threadList.currentIndex && mainView.bottomEdge.status !== BottomEdge.Committed |
1405 | + } |
1406 | + } |
1407 | + |
1408 | searchTerm: mainPage.searching ? searchField.text : "" |
1409 | onItemClicked: { |
1410 | if (threadList.isInSelectionMode) { |
1411 | @@ -222,7 +263,10 @@ |
1412 | if (displayedEvent != null) { |
1413 | properties["scrollToEventId"] = displayedEvent.eventId |
1414 | } |
1415 | - mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) |
1416 | + mainStack.addPageToNextColumn(mainPage, messagesWithBottomEdge, properties) |
1417 | + |
1418 | + // mark this item as current |
1419 | + threadList.currentIndex = index |
1420 | } |
1421 | } |
1422 | onItemPressAndHold: { |
1423 | @@ -242,6 +286,13 @@ |
1424 | mainView.removeThreads(threadsToRemove); |
1425 | } |
1426 | } |
1427 | + |
1428 | + Binding { |
1429 | + target: threadList |
1430 | + property: 'contentY' |
1431 | + value: -threadList.headerItem.height |
1432 | + when: mainView.composingNewMessage |
1433 | + } |
1434 | } |
1435 | |
1436 | KeyboardRectangle { |
1437 | @@ -252,4 +303,12 @@ |
1438 | flickableItem: threadList |
1439 | align: Qt.AlignTrailing |
1440 | } |
1441 | + |
1442 | + Loader { |
1443 | + id: bottomEdgeLoader |
1444 | + active: !selectionMode && !searching && !mainView.dualPanel |
1445 | + sourceComponent: MessagingBottomEdge { |
1446 | + parent: mainPage |
1447 | + } |
1448 | + } |
1449 | } |
1450 | |
1451 | === modified file 'src/qml/Messages.qml' |
1452 | --- src/qml/Messages.qml 2016-01-28 15:00:52 +0000 |
1453 | +++ src/qml/Messages.qml 2016-01-28 15:00:52 +0000 |
1454 | @@ -63,6 +63,19 @@ |
1455 | property QtObject presenceRequest: presenceItem |
1456 | property var accountsModel: getAccountsModel() |
1457 | property alias oskEnabled: keyboard.oskEnabled |
1458 | + property bool isReady: false |
1459 | + property string firstRecipientAlias: ((contactWatcher.isUnknown && |
1460 | + contactWatcher.isInteractive) || |
1461 | + contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias |
1462 | + |
1463 | + |
1464 | + // When using this view from the bottom edge, we are not in the stack, so we need to push on top of the parent page |
1465 | + property var basePage: messages |
1466 | + |
1467 | + property bool startedFromBottomEdge: false |
1468 | + |
1469 | + signal ready |
1470 | + signal cancel |
1471 | |
1472 | function getAccountsModel() { |
1473 | var accounts = [] |
1474 | @@ -221,99 +234,6 @@ |
1475 | return thread |
1476 | } |
1477 | |
1478 | - Connections { |
1479 | - target: telepathyHelper |
1480 | - onSetupReady: { |
1481 | - // force reevaluation |
1482 | - messages.account = Qt.binding(getCurrentAccount) |
1483 | - messages.phoneAccount = Qt.binding(isPhoneAccount) |
1484 | - head.sections.model = Qt.binding(getSectionsModel) |
1485 | - head.sections.selectedIndex = Qt.binding(getSelectedIndex) |
1486 | - } |
1487 | - } |
1488 | - |
1489 | - |
1490 | - Connections { |
1491 | - target: chatManager |
1492 | - onChatEntryCreated: { |
1493 | - // TODO: track using chatId and not participants |
1494 | - if (accountId == account.accountId && |
1495 | - firstParticipant && participants[0] == firstParticipant.identifier) { |
1496 | - messages.chatEntry = chatEntry |
1497 | - } |
1498 | - } |
1499 | - onChatsChanged: { |
1500 | - for (var i in chatManager.chats) { |
1501 | - var chat = chatManager.chats[i] |
1502 | - // TODO: track using chatId and not participants |
1503 | - if (chat.account.accountId == account.accountId && |
1504 | - firstParticipant && chat.participants[0] == firstParticipant.identifier) { |
1505 | - messages.chatEntry = chat |
1506 | - return |
1507 | - } |
1508 | - } |
1509 | - messages.chatEntry = null |
1510 | - } |
1511 | - } |
1512 | - |
1513 | - Timer { |
1514 | - id: typingTimer |
1515 | - interval: 6000 |
1516 | - onTriggered: { |
1517 | - messages.userTyping = false; |
1518 | - } |
1519 | - } |
1520 | - |
1521 | - Repeater { |
1522 | - model: messages.chatEntry ? messages.chatEntry.chatStates : null |
1523 | - Item { |
1524 | - function processChatState() { |
1525 | - if (modelData.state == ChatEntry.ChannelChatStateComposing) { |
1526 | - messages.userTyping = true |
1527 | - typingTimer.start() |
1528 | - } else { |
1529 | - messages.userTyping = false |
1530 | - } |
1531 | - } |
1532 | - Component.onCompleted: processChatState() |
1533 | - Connections { |
1534 | - target: modelData |
1535 | - onStateChanged: processChatState() |
1536 | - } |
1537 | - } |
1538 | - } |
1539 | - |
1540 | - MessagesHeader { |
1541 | - id: header |
1542 | - width: parent ? parent.width - units.gu(2) : undefined |
1543 | - height: units.gu(5) |
1544 | - title: messages.title |
1545 | - subtitle: { |
1546 | - if (userTyping) { |
1547 | - return i18n.tr("Typing..") |
1548 | - } |
1549 | - switch (presenceRequest.type) { |
1550 | - case PresenceRequest.PresenceTypeAvailable: |
1551 | - return i18n.tr("Online") |
1552 | - case PresenceRequest.PresenceTypeOffline: |
1553 | - return i18n.tr("Offline") |
1554 | - case PresenceRequest.PresenceTypeAway: |
1555 | - return i18n.tr("Away") |
1556 | - case PresenceRequest.PresenceTypeBusy: |
1557 | - return i18n.tr("Busy") |
1558 | - default: |
1559 | - return "" |
1560 | - } |
1561 | - } |
1562 | - visible: true |
1563 | - } |
1564 | - |
1565 | - head { |
1566 | - id: head |
1567 | - sections.model: getSectionsModel() |
1568 | - sections.selectedIndex: getSelectedIndex() |
1569 | - } |
1570 | - |
1571 | function sendMessageNetworkCheck() { |
1572 | if (messages.account.simLocked) { |
1573 | Qt.inputMethod.hide() |
1574 | @@ -363,7 +283,7 @@ |
1575 | if (event.senderId == "self" && event.accountId != messages.account.accountId) { |
1576 | var tmpAccount = telepathyHelper.accountForId(event.accountId) |
1577 | if (!tmpAccount || (tmpAccount.type == AccountEntry.MultimediaAccount && messages.account.type == AccountEntry.PhoneAccount)) { |
1578 | - // we don't add the information event if the last outgoing message |
1579 | + // we don't add the information event if the last outgoing message |
1580 | // was a fallback to a multimedia service |
1581 | break; |
1582 | } |
1583 | @@ -425,7 +345,7 @@ |
1584 | var isSelfContactKnown = account.selfContactId != "" |
1585 | if (isMmsGroupChat && !isSelfContactKnown) { |
1586 | // TODO: inform the user to enter the phone number of the selected sim card manually |
1587 | - // and use it in the telepathy-ofono account as selfContactId. |
1588 | + // and use it in the telepathy-ofono account as selfContactId. |
1589 | return false |
1590 | } |
1591 | var fallbackAccountId = chatManager.sendMessage(messages.account.accountId, participantIds, text, attachments, properties) |
1592 | @@ -448,102 +368,6 @@ |
1593 | return true |
1594 | } |
1595 | |
1596 | - PresenceRequest { |
1597 | - id: presenceItem |
1598 | - accountId: { |
1599 | - // if this is a regular sms chat, try requesting the presence on |
1600 | - // a multimedia account |
1601 | - if (!account) { |
1602 | - return "" |
1603 | - } |
1604 | - if (account.type == AccountEntry.PhoneAccount) { |
1605 | - for (var i in telepathyHelper.accounts) { |
1606 | - var tmpAccount = telepathyHelper.accounts[i] |
1607 | - if (tmpAccount.type == AccountEntry.MultimediaAccount) { |
1608 | - return tmpAccount.accountId |
1609 | - } |
1610 | - } |
1611 | - return "" |
1612 | - } |
1613 | - return account.accountId |
1614 | - } |
1615 | - // we just request presence on 1-1 chats |
1616 | - identifier: participants.length == 1 ? participants[0].identifier : "" |
1617 | - } |
1618 | - |
1619 | - // this is necessary to automatically update the view when the |
1620 | - // default account changes in system settings |
1621 | - Connections { |
1622 | - target: mainView |
1623 | - onAccountChanged: { |
1624 | - if (!messages.phoneAccount) { |
1625 | - return |
1626 | - } |
1627 | - messages.account = mainView.account |
1628 | - } |
1629 | - } |
1630 | - |
1631 | - ActivityIndicator { |
1632 | - id: activityIndicator |
1633 | - anchors { |
1634 | - verticalCenter: parent.verticalCenter |
1635 | - horizontalCenter: parent.horizontalCenter |
1636 | - } |
1637 | - running: isSearching |
1638 | - visible: running |
1639 | - } |
1640 | - |
1641 | - flickable: null |
1642 | - |
1643 | - property bool isReady: false |
1644 | - signal ready |
1645 | - onReady: { |
1646 | - isReady = true |
1647 | - if (participants.length === 0 && keyboardFocus) |
1648 | - multiRecipient.forceFocus() |
1649 | - } |
1650 | - |
1651 | - property string firstRecipientAlias: ((contactWatcher.isUnknown && |
1652 | - contactWatcher.isInteractive) || |
1653 | - contactWatcher.alias === "") ? contactWatcher.identifier : contactWatcher.alias |
1654 | - title: { |
1655 | - if (selectionMode || participants.length == 0) { |
1656 | - return " " |
1657 | - } |
1658 | - |
1659 | - if (landscape) { |
1660 | - return "" |
1661 | - } |
1662 | - if (participants.length > 0) { |
1663 | - if (participants.length == 1) { |
1664 | - return firstRecipientAlias |
1665 | - } else { |
1666 | - // TRANSLATORS: %1 refers to the number of participants in a group chat |
1667 | - return i18n.tr("Group (%1)").arg(participants.length) |
1668 | - } |
1669 | - } |
1670 | - return i18n.tr("New Message") |
1671 | - } |
1672 | - |
1673 | - Component.onCompleted: { |
1674 | - if (messages.accountId !== "") { |
1675 | - var account = telepathyHelper.accountForId(messages.accountId) |
1676 | - if (account && account.type == AccountEntry.MultimediaAccount) { |
1677 | - // fallback the first available phone account |
1678 | - if (telepathyHelper.phoneAccounts.length > 0) { |
1679 | - messages.accountId = telepathyHelper.phoneAccounts[0].accountId |
1680 | - } |
1681 | - } |
1682 | - } |
1683 | - composeBar.addAttachments(sharedAttachmentsTransfer) |
1684 | - } |
1685 | - |
1686 | - onActiveChanged: { |
1687 | - if (active && (eventModel.count > 0)){ |
1688 | - swipeItemDemo.enable() |
1689 | - } |
1690 | - } |
1691 | - |
1692 | function updateFilters(accounts, participants, reload, threads) { |
1693 | if (participants.length == 0 || accounts.length == 0) { |
1694 | return null |
1695 | @@ -603,8 +427,322 @@ |
1696 | return eventModel.markEventAsRead(accountId, threadId, eventId, type); |
1697 | } |
1698 | |
1699 | + header: PageHeader { |
1700 | + id: pageHeader |
1701 | + |
1702 | + property alias leadingActions: leadingBar.actions |
1703 | + property alias trailingActions: trailingBar.actions |
1704 | + |
1705 | + title: { |
1706 | + if (landscape) { |
1707 | + return "" |
1708 | + } |
1709 | + |
1710 | + if (participants.length == 1) { |
1711 | + return firstRecipientAlias |
1712 | + } |
1713 | + |
1714 | + return i18n.tr("New Message") |
1715 | + } |
1716 | + |
1717 | + Sections { |
1718 | + id: sections |
1719 | + anchors { |
1720 | + left: parent.left |
1721 | + leftMargin: units.gu(2) |
1722 | + bottom: parent.bottom |
1723 | + } |
1724 | + model: getSectionsModel() |
1725 | + selectedIndex: getSelectedIndex() |
1726 | + onSelectedIndexChanged: { |
1727 | + if (selectedIndex >= 0) { |
1728 | + messages.account = messages.accountsModel[selectedIndex] |
1729 | + } |
1730 | + } |
1731 | + } |
1732 | + |
1733 | + extension: sections.model.length > 1 ? sections : null |
1734 | + |
1735 | + leadingActionBar { |
1736 | + id: leadingBar |
1737 | + |
1738 | + property list<QtObject> bottomEdgeLeadingActions: [ |
1739 | + Action { |
1740 | + id: backAction |
1741 | + |
1742 | + objectName: "cancel" |
1743 | + name: "cancel" |
1744 | + text: i18n.tr("Cancel") |
1745 | + iconName: "down" |
1746 | + shortcut: "Esc" |
1747 | + onTriggered: { |
1748 | + messages.cancel() |
1749 | + } |
1750 | + } |
1751 | + ] |
1752 | + |
1753 | + actions: startedFromBottomEdge ? bottomEdgeLeadingActions : [] |
1754 | + } |
1755 | + |
1756 | + trailingActionBar { |
1757 | + id: trailingBar |
1758 | + } |
1759 | + } |
1760 | + |
1761 | + states: [ |
1762 | + State { |
1763 | + id: selectionState |
1764 | + name: "selection" |
1765 | + when: selectionMode |
1766 | + |
1767 | + property list<QtObject> leadingActions: [ |
1768 | + Action { |
1769 | + objectName: "selectionModeCancelAction" |
1770 | + iconName: "back" |
1771 | + onTriggered: messageList.cancelSelection() |
1772 | + } |
1773 | + ] |
1774 | + |
1775 | + property list<QtObject> trailingActions: [ |
1776 | + Action { |
1777 | + objectName: "selectionModeSelectAllAction" |
1778 | + iconName: "select" |
1779 | + onTriggered: { |
1780 | + if (messageList.selectedItems.count === messageList.count) { |
1781 | + messageList.clearSelection() |
1782 | + } else { |
1783 | + messageList.selectAll() |
1784 | + } |
1785 | + } |
1786 | + }, |
1787 | + Action { |
1788 | + objectName: "selectionModeDeleteAction" |
1789 | + enabled: messageList.selectedItems.count > 0 |
1790 | + iconName: "delete" |
1791 | + onTriggered: messageList.endSelection() |
1792 | + } |
1793 | + ] |
1794 | + |
1795 | + PropertyChanges { |
1796 | + target: pageHeader |
1797 | + title: " " |
1798 | + leadingActions: selectionState.leadingActions |
1799 | + trailingActions: selectionState.trailingActions |
1800 | + } |
1801 | + }, |
1802 | + State { |
1803 | + id: groupChatState |
1804 | + name: "groupChat" |
1805 | + when: groupChat |
1806 | + |
1807 | + property list<QtObject> trailingActions: [ |
1808 | + Action { |
1809 | + objectName: "groupChatAction" |
1810 | + iconName: "contact-group" |
1811 | + onTriggered: PopupUtils.open(participantsPopover, pageHeader) |
1812 | + } |
1813 | + ] |
1814 | + |
1815 | + PropertyChanges { |
1816 | + target: pageHeader |
1817 | + // TRANSLATORS: %1 refers to the number of participants in a group chat |
1818 | + title: i18n.tr("Group (%1)").arg(participants.length) |
1819 | + contents: headerContents |
1820 | + trailingActions: groupChatState.trailingActions |
1821 | + } |
1822 | + }, |
1823 | + State { |
1824 | + id: unknownContactState |
1825 | + name: "unknownContact" |
1826 | + when: participants.length == 1 && contactWatcher.isUnknown |
1827 | + |
1828 | + property list<QtObject> trailingActions: [ |
1829 | + Action { |
1830 | + objectName: "contactCallAction" |
1831 | + visible: participants.length == 1 && contactWatcher.interactive |
1832 | + iconName: "call-start" |
1833 | + text: i18n.tr("Call") |
1834 | + onTriggered: { |
1835 | + Qt.inputMethod.hide() |
1836 | + // FIXME: support other things than just phone numbers |
1837 | + Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1838 | + } |
1839 | + }, |
1840 | + Action { |
1841 | + objectName: "addContactAction" |
1842 | + visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive |
1843 | + iconName: "contact-new" |
1844 | + text: i18n.tr("Add") |
1845 | + onTriggered: { |
1846 | + Qt.inputMethod.hide() |
1847 | + // FIXME: support other things than just phone numbers |
1848 | + mainView.addPhoneToContact(messages, "", contactWatcher.identifier, null, null) |
1849 | + } |
1850 | + } |
1851 | + ] |
1852 | + PropertyChanges { |
1853 | + target: pageHeader |
1854 | + contents: headerContents |
1855 | + trailingActions: unknownContactState.trailingActions |
1856 | + } |
1857 | + }, |
1858 | + State { |
1859 | + id: newMessageState |
1860 | + name: "newMessage" |
1861 | + when: participants.length === 0 |
1862 | + |
1863 | + property list<QtObject> trailingActions: [ |
1864 | + Action { |
1865 | + objectName: "contactList" |
1866 | + iconName: "contact" |
1867 | + onTriggered: { |
1868 | + Qt.inputMethod.hide() |
1869 | + mainStack.addPageToCurrentColumn(basePage, Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient, "parentPage": messages}) |
1870 | + } |
1871 | + } |
1872 | + |
1873 | + ] |
1874 | + |
1875 | + property Item contents: MultiRecipientInput { |
1876 | + id: multiRecipient |
1877 | + objectName: "multiRecipient" |
1878 | + enabled: visible |
1879 | + anchors { |
1880 | + left: parent ? parent.left : undefined |
1881 | + right: parent ? parent.right : undefined |
1882 | + rightMargin: units.gu(2) |
1883 | + top: parent ? parent.top: undefined |
1884 | + topMargin: units.gu(1) |
1885 | + } |
1886 | + focus: true |
1887 | + |
1888 | + Connections { |
1889 | + target: mainView.bottomEdge |
1890 | + onStatusChanged: { |
1891 | + if (mainView.bottomEdge.status === BottomEdge.Committed) { |
1892 | + multiRecipient.forceFocus() |
1893 | + } |
1894 | + } |
1895 | + } |
1896 | + } |
1897 | + |
1898 | + PropertyChanges { |
1899 | + target: pageHeader |
1900 | + title: " " |
1901 | + trailingActions: newMessageState.trailingActions |
1902 | + contents: newMessageState.contents |
1903 | + } |
1904 | + }, |
1905 | + State { |
1906 | + id: knownContactState |
1907 | + name: "knownContact" |
1908 | + when: participants.length == 1 && !contactWatcher.isUnknown |
1909 | + property list<QtObject> trailingActions: [ |
1910 | + Action { |
1911 | + objectName: "contactCallKnownAction" |
1912 | + visible: participants.length == 1 && messages.phoneAccount |
1913 | + iconName: "call-start" |
1914 | + text: i18n.tr("Call") |
1915 | + onTriggered: { |
1916 | + Qt.inputMethod.hide() |
1917 | + // FIXME: support other things than just phone numbers |
1918 | + Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
1919 | + } |
1920 | + }, |
1921 | + Action { |
1922 | + objectName: "contactProfileAction" |
1923 | + visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount |
1924 | + iconSource: "image://theme/contact" |
1925 | + text: i18n.tr("Contact") |
1926 | + onTriggered: { |
1927 | + mainView.showContactDetails(messages, contactWatcher.contactId, null, null) |
1928 | + } |
1929 | + } |
1930 | + ] |
1931 | + PropertyChanges { |
1932 | + target: pageHeader |
1933 | + contents: headerContents |
1934 | + trailingActions: knownContactState.trailingActions |
1935 | + } |
1936 | + } |
1937 | + ] |
1938 | + |
1939 | + Component.onCompleted: { |
1940 | + if (messages.accountId !== "") { |
1941 | + var account = telepathyHelper.accountForId(messages.accountId) |
1942 | + if (account && account.type == AccountEntry.MultimediaAccount) { |
1943 | + // fallback the first available phone account |
1944 | + if (telepathyHelper.phoneAccounts.length > 0) { |
1945 | + messages.accountId = telepathyHelper.phoneAccounts[0].accountId |
1946 | + } |
1947 | + } |
1948 | + } |
1949 | + composeBar.addAttachments(sharedAttachmentsTransfer) |
1950 | + } |
1951 | + |
1952 | + Component.onDestruction: { |
1953 | + if (!mainView.dualPanel) { |
1954 | + mainPage.displayedThreadIndex = -1 |
1955 | + } |
1956 | + } |
1957 | + |
1958 | + onReady: { |
1959 | + isReady = true |
1960 | + if (participants.length === 0 && keyboardFocus) |
1961 | + multiRecipient.forceFocus() |
1962 | + } |
1963 | + |
1964 | + onActiveChanged: { |
1965 | + if (active && (eventModel.count > 0)){ |
1966 | + swipeItemDemo.enable() |
1967 | + } |
1968 | + } |
1969 | + |
1970 | + Connections { |
1971 | + target: telepathyHelper |
1972 | + onSetupReady: { |
1973 | + // force reevaluation |
1974 | + messages.account = Qt.binding(getCurrentAccount) |
1975 | + messages.phoneAccount = Qt.binding(isPhoneAccount) |
1976 | + head.sections.model = Qt.binding(getSectionsModel) |
1977 | + head.sections.selectedIndex = Qt.binding(getSelectedIndex) |
1978 | + } |
1979 | + } |
1980 | + |
1981 | + Connections { |
1982 | + target: chatManager |
1983 | + onChatEntryCreated: { |
1984 | + // TODO: track using chatId and not participants |
1985 | + if (accountId == account.accountId && |
1986 | + firstParticipant && participants[0] == firstParticipant.identifier) { |
1987 | + messages.chatEntry = chatEntry |
1988 | + } |
1989 | + } |
1990 | + onChatsChanged: { |
1991 | + for (var i in chatManager.chats) { |
1992 | + var chat = chatManager.chats[i] |
1993 | + // TODO: track using chatId and not participants |
1994 | + if (chat.account.accountId == account.accountId && |
1995 | + firstParticipant && chat.participants[0] == firstParticipant.identifier) { |
1996 | + messages.chatEntry = chat |
1997 | + return |
1998 | + } |
1999 | + } |
2000 | + messages.chatEntry = null |
2001 | + } |
2002 | + } |
2003 | + |
2004 | + // this is necessary to automatically update the view when the |
2005 | + // default account changes in system settings |
2006 | Connections { |
2007 | target: mainView |
2008 | + onAccountChanged: { |
2009 | + if (!messages.phoneAccount) { |
2010 | + return |
2011 | + } |
2012 | + messages.account = mainView.account |
2013 | + } |
2014 | + |
2015 | onApplicationActiveChanged: { |
2016 | if (mainView.applicationActive) { |
2017 | for (var i in pendingEventsToMarkAsRead) { |
2018 | @@ -616,6 +754,91 @@ |
2019 | } |
2020 | } |
2021 | |
2022 | + Timer { |
2023 | + id: typingTimer |
2024 | + interval: 6000 |
2025 | + onTriggered: { |
2026 | + messages.userTyping = false; |
2027 | + } |
2028 | + } |
2029 | + |
2030 | + Repeater { |
2031 | + model: messages.chatEntry ? messages.chatEntry.chatStates : null |
2032 | + Item { |
2033 | + function processChatState() { |
2034 | + if (modelData.state == ChatEntry.ChannelChatStateComposing) { |
2035 | + messages.userTyping = true |
2036 | + typingTimer.start() |
2037 | + } else { |
2038 | + messages.userTyping = false |
2039 | + } |
2040 | + } |
2041 | + Component.onCompleted: processChatState() |
2042 | + Connections { |
2043 | + target: modelData |
2044 | + onStateChanged: processChatState() |
2045 | + } |
2046 | + } |
2047 | + } |
2048 | + |
2049 | + MessagesHeader { |
2050 | + id: headerContents |
2051 | + width: parent ? parent.width - units.gu(2) : undefined |
2052 | + height: units.gu(5) |
2053 | + title: pageHeader.title |
2054 | + subtitle: { |
2055 | + if (userTyping) { |
2056 | + return i18n.tr("Typing..") |
2057 | + } |
2058 | + switch (presenceRequest.type) { |
2059 | + case PresenceRequest.PresenceTypeAvailable: |
2060 | + return i18n.tr("Online") |
2061 | + case PresenceRequest.PresenceTypeOffline: |
2062 | + return i18n.tr("Offline") |
2063 | + case PresenceRequest.PresenceTypeAway: |
2064 | + return i18n.tr("Away") |
2065 | + case PresenceRequest.PresenceTypeBusy: |
2066 | + return i18n.tr("Busy") |
2067 | + default: |
2068 | + return "" |
2069 | + } |
2070 | + } |
2071 | + visible: true |
2072 | + } |
2073 | + |
2074 | + PresenceRequest { |
2075 | + id: presenceItem |
2076 | + accountId: { |
2077 | + // if this is a regular sms chat, try requesting the presence on |
2078 | + // a multimedia account |
2079 | + if (!account) { |
2080 | + return "" |
2081 | + } |
2082 | + if (account.type == AccountEntry.PhoneAccount) { |
2083 | + for (var i in telepathyHelper.accounts) { |
2084 | + var tmpAccount = telepathyHelper.accounts[i] |
2085 | + if (tmpAccount.type == AccountEntry.MultimediaAccount) { |
2086 | + return tmpAccount.accountId |
2087 | + } |
2088 | + } |
2089 | + return "" |
2090 | + } |
2091 | + return account.accountId |
2092 | + } |
2093 | + // we just request presence on 1-1 chats |
2094 | + identifier: participants.length == 1 ? participants[0].identifier : "" |
2095 | + } |
2096 | + |
2097 | + ActivityIndicator { |
2098 | + id: activityIndicator |
2099 | + anchors { |
2100 | + verticalCenter: parent.verticalCenter |
2101 | + horizontalCenter: parent.horizontalCenter |
2102 | + } |
2103 | + running: isSearching |
2104 | + visible: running |
2105 | + } |
2106 | + |
2107 | Component { |
2108 | id: participantsPopover |
2109 | |
2110 | @@ -678,13 +901,6 @@ |
2111 | } |
2112 | } |
2113 | |
2114 | - Connections { |
2115 | - target: messages.head.sections |
2116 | - onSelectedIndexChanged: { |
2117 | - messages.account = messages.accountsModel[head.sections.selectedIndex] |
2118 | - } |
2119 | - } |
2120 | - |
2121 | Loader { |
2122 | id: searchListLoader |
2123 | |
2124 | @@ -696,7 +912,7 @@ |
2125 | visible: source != "" |
2126 | anchors { |
2127 | top: parent.top |
2128 | - topMargin: units.gu(2) |
2129 | + topMargin: header.height + units.gu(2) |
2130 | left: parent.left |
2131 | right: parent.right |
2132 | bottom: composeBar.top |
2133 | @@ -756,156 +972,6 @@ |
2134 | addressableFields: messages.account ? messages.account.addressableVCardFields : ["tel"] // just to have a fallback there |
2135 | } |
2136 | |
2137 | - Action { |
2138 | - id: backButton |
2139 | - objectName: "backButton" |
2140 | - iconName: "back" |
2141 | - onTriggered: { |
2142 | - if (typeof mainPage !== 'undefined') { |
2143 | - mainPage.temporaryProperties = null |
2144 | - } |
2145 | - mainStack.pop() |
2146 | - } |
2147 | - } |
2148 | - |
2149 | - states: [ |
2150 | - PageHeadState { |
2151 | - name: "selection" |
2152 | - head: messages.head |
2153 | - when: selectionMode |
2154 | - |
2155 | - backAction: Action { |
2156 | - objectName: "selectionModeCancelAction" |
2157 | - iconName: "back" |
2158 | - onTriggered: messageList.cancelSelection() |
2159 | - } |
2160 | - |
2161 | - actions: [ |
2162 | - Action { |
2163 | - objectName: "selectionModeSelectAllAction" |
2164 | - iconName: "select" |
2165 | - onTriggered: { |
2166 | - if (messageList.selectedItems.count === messageList.count) { |
2167 | - messageList.clearSelection() |
2168 | - } else { |
2169 | - messageList.selectAll() |
2170 | - } |
2171 | - } |
2172 | - }, |
2173 | - Action { |
2174 | - objectName: "selectionModeDeleteAction" |
2175 | - enabled: messageList.selectedItems.count > 0 |
2176 | - iconName: "delete" |
2177 | - onTriggered: messageList.endSelection() |
2178 | - } |
2179 | - ] |
2180 | - }, |
2181 | - PageHeadState { |
2182 | - name: "groupChat" |
2183 | - head: messages.head |
2184 | - when: groupChat |
2185 | - contents: header |
2186 | - backAction: backButton |
2187 | - |
2188 | - actions: [ |
2189 | - Action { |
2190 | - objectName: "groupChatAction" |
2191 | - iconName: "contact-group" |
2192 | - onTriggered: PopupUtils.open(participantsPopover, screenTop) |
2193 | - } |
2194 | - ] |
2195 | - }, |
2196 | - PageHeadState { |
2197 | - name: "unknownContact" |
2198 | - head: messages.head |
2199 | - when: participants.length == 1 && contactWatcher.isUnknown |
2200 | - backAction: backButton |
2201 | - contents: header |
2202 | - |
2203 | - actions: [ |
2204 | - Action { |
2205 | - objectName: "contactCallAction" |
2206 | - visible: participants.length == 1 && contactWatcher.interactive |
2207 | - iconName: "call-start" |
2208 | - text: i18n.tr("Call") |
2209 | - onTriggered: { |
2210 | - Qt.inputMethod.hide() |
2211 | - // FIXME: support other things than just phone numbers |
2212 | - Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
2213 | - } |
2214 | - }, |
2215 | - Action { |
2216 | - objectName: "addContactAction" |
2217 | - visible: contactWatcher.isUnknown && participants.length == 1 && contactWatcher.interactive |
2218 | - iconName: "contact-new" |
2219 | - text: i18n.tr("Add") |
2220 | - onTriggered: { |
2221 | - Qt.inputMethod.hide() |
2222 | - // FIXME: support other things than just phone numbers |
2223 | - mainView.addPhoneToContact("", contactWatcher.identifier, null, null) |
2224 | - } |
2225 | - } |
2226 | - ] |
2227 | - }, |
2228 | - PageHeadState { |
2229 | - name: "newMessage" |
2230 | - head: messages.head |
2231 | - when: participants.length === 0 && isReady |
2232 | - backAction: backButton |
2233 | - actions: [ |
2234 | - Action { |
2235 | - objectName: "contactList" |
2236 | - iconName: "contact" |
2237 | - onTriggered: { |
2238 | - Qt.inputMethod.hide() |
2239 | - mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), {"multiRecipient": multiRecipient, "parentPage": messages}) |
2240 | - } |
2241 | - } |
2242 | - |
2243 | - ] |
2244 | - |
2245 | - contents: MultiRecipientInput { |
2246 | - id: multiRecipient |
2247 | - objectName: "multiRecipient" |
2248 | - enabled: visible |
2249 | - anchors { |
2250 | - left: parent ? parent.left : undefined |
2251 | - right: parent ? parent.right : undefined |
2252 | - rightMargin: units.gu(2) |
2253 | - } |
2254 | - } |
2255 | - }, |
2256 | - PageHeadState { |
2257 | - name: "knownContact" |
2258 | - head: messages.head |
2259 | - when: participants.length == 1 && !contactWatcher.isUnknown |
2260 | - backAction: backButton |
2261 | - contents: header |
2262 | - actions: [ |
2263 | - Action { |
2264 | - objectName: "contactCallKnownAction" |
2265 | - visible: participants.length == 1 && messages.phoneAccount |
2266 | - iconName: "call-start" |
2267 | - text: i18n.tr("Call") |
2268 | - onTriggered: { |
2269 | - Qt.inputMethod.hide() |
2270 | - // FIXME: support other things than just phone numbers |
2271 | - Qt.openUrlExternally("tel:///" + encodeURIComponent(contactWatcher.identifier)) |
2272 | - } |
2273 | - }, |
2274 | - Action { |
2275 | - objectName: "contactProfileAction" |
2276 | - visible: !contactWatcher.isUnknown && participants.length == 1 && messages.phoneAccount |
2277 | - iconSource: "image://theme/contact" |
2278 | - text: i18n.tr("Contact") |
2279 | - onTriggered: { |
2280 | - mainView.showContactDetails(contactWatcher.contactId, null, null) |
2281 | - } |
2282 | - } |
2283 | - ] |
2284 | - } |
2285 | - ] |
2286 | - |
2287 | HistoryEventModel { |
2288 | id: eventModel |
2289 | type: HistoryThreadModel.EventTypeText |
2290 | @@ -958,7 +1024,7 @@ |
2291 | Item { |
2292 | id: screenTop |
2293 | anchors { |
2294 | - top: parent.top |
2295 | + top: pageHeader.bottom |
2296 | left: parent.left |
2297 | right: parent.right |
2298 | } |
2299 | @@ -1115,4 +1181,6 @@ |
2300 | } |
2301 | } |
2302 | } |
2303 | + |
2304 | + // FIXME: we need a bottom edge here somehow |
2305 | } |
2306 | |
2307 | === modified file 'src/qml/MessagesHeader.qml' |
2308 | --- src/qml/MessagesHeader.qml 2016-01-28 15:00:52 +0000 |
2309 | +++ src/qml/MessagesHeader.qml 2016-01-28 15:00:52 +0000 |
2310 | @@ -26,7 +26,12 @@ |
2311 | property string title: "" |
2312 | property string subtitle: "" |
2313 | |
2314 | - height: units.gu(7) |
2315 | + height: units.gu(8) |
2316 | + |
2317 | + anchors { |
2318 | + top: parent.top |
2319 | + topMargin: units.gu(1) |
2320 | + } |
2321 | |
2322 | Behavior on height { |
2323 | UbuntuNumberAnimation {} |
2324 | @@ -50,7 +55,7 @@ |
2325 | } |
2326 | verticalAlignment: Text.AlignVCenter |
2327 | |
2328 | - font.pixelSize: FontUtils.sizeToPixels("x-large") |
2329 | + font.pixelSize: FontUtils.sizeToPixels("large") |
2330 | elide: Text.ElideRight |
2331 | text: title |
2332 | } |
2333 | |
2334 | === added file 'src/qml/MessagingBottomEdge.qml' |
2335 | --- src/qml/MessagingBottomEdge.qml 1970-01-01 00:00:00 +0000 |
2336 | +++ src/qml/MessagingBottomEdge.qml 2016-01-28 15:00:52 +0000 |
2337 | @@ -0,0 +1,43 @@ |
2338 | +import QtQuick 2.0 |
2339 | +import Ubuntu.Components 1.3 |
2340 | + |
2341 | +BottomEdge { |
2342 | + id: bottomEdge |
2343 | + |
2344 | + function commitWithProperties(properties) { |
2345 | + _realPage = messagesComponent.createObject(null, properties) |
2346 | + commit() |
2347 | + } |
2348 | + |
2349 | + property var _realPage: null |
2350 | + |
2351 | + height: parent ? parent.height : 0 |
2352 | + hint.text: i18n.tr("+") |
2353 | + contentComponent: Item { |
2354 | + id: pageContent |
2355 | + implicitWidth: bottomEdge.width |
2356 | + implicitHeight: bottomEdge.height |
2357 | + children: bottomEdge._realPage |
2358 | + } |
2359 | + |
2360 | + Component.onCompleted: { |
2361 | + mainView.bottomEdge = bottomEdge |
2362 | + _realPage = messagesComponent.createObject(null) |
2363 | + } |
2364 | + |
2365 | + onCollapseCompleted: { |
2366 | + _realPage = messagesComponent.createObject(null) |
2367 | + } |
2368 | + |
2369 | + Component { |
2370 | + id: messagesComponent |
2371 | + |
2372 | + Messages { |
2373 | + anchors.fill: parent |
2374 | + onCancel: bottomEdge.collapse() |
2375 | + basePage: bottomEdge.parent |
2376 | + startedFromBottomEdge: true |
2377 | + } |
2378 | + } |
2379 | + |
2380 | +} |
2381 | |
2382 | === modified file 'src/qml/MessagingContactEditorPage.qml' |
2383 | --- src/qml/MessagingContactEditorPage.qml 2016-01-06 18:29:47 +0000 |
2384 | +++ src/qml/MessagingContactEditorPage.qml 2016-01-28 15:00:52 +0000 |
2385 | @@ -52,8 +52,12 @@ |
2386 | |
2387 | onContactSaved: { |
2388 | if (root.contactListPage) { |
2389 | - root.contactListPage.moveListToContact(contact) |
2390 | - root.contactListPage.phoneToAdd = "" |
2391 | + if (root.contactListPage.phoneToAdd !== "") { |
2392 | + mainStack.removePages(root.contactListPage) |
2393 | + } else { |
2394 | + root.contactListPage.moveListToContact(contact) |
2395 | + root.contactListPage.phoneToAdd = "" |
2396 | + } |
2397 | } |
2398 | } |
2399 | } |
2400 | |
2401 | === modified file 'src/qml/MessagingContactViewPage.qml' |
2402 | --- src/qml/MessagingContactViewPage.qml 2016-01-06 18:29:47 +0000 |
2403 | +++ src/qml/MessagingContactViewPage.qml 2016-01-28 15:00:52 +0000 |
2404 | @@ -42,12 +42,13 @@ |
2405 | var newDetail = Qt.createQmlObject(detailSourceTemplate, contact) |
2406 | if (newDetail) { |
2407 | contact.addDetail(newDetail) |
2408 | - pageStack.push(root.contactEditorPageURL, |
2409 | - { model: root.model, |
2410 | - contact: contact, |
2411 | - initialFocusSection: "phones", |
2412 | - newDetails: [newDetail], |
2413 | - contactListPage: root.contactListPage }) |
2414 | + mainStack.addPageToCurrentColumn(root, |
2415 | + root.contactEditorPageURL, |
2416 | + { model: root.model, |
2417 | + contact: contact, |
2418 | + initialFocusSection: "phones", |
2419 | + newDetails: [newDetail], |
2420 | + contactListPage: root.contactListPage }) |
2421 | root.addPhoneToContact = "" |
2422 | } else { |
2423 | console.warn("Fail to create phone number detail") |
2424 | |
2425 | === modified file 'src/qml/MultiRecipientInput.qml' |
2426 | --- src/qml/MultiRecipientInput.qml 2015-09-14 13:51:27 +0000 |
2427 | +++ src/qml/MultiRecipientInput.qml 2016-01-28 15:00:52 +0000 |
2428 | @@ -29,7 +29,7 @@ |
2429 | property variant recipients: [] |
2430 | property string searchString: "" |
2431 | signal clearSearch() |
2432 | - style: Theme.createStyleComponent("TextFieldStyle.qml", multiRecipientWidget) |
2433 | + styleName: "TextFieldStyle" |
2434 | clip: true |
2435 | height: contactFlow.height |
2436 | focus: activeFocus |
2437 | |
2438 | === modified file 'src/qml/NewRecipientPage.qml' |
2439 | --- src/qml/NewRecipientPage.qml 2015-11-16 22:16:06 +0000 |
2440 | +++ src/qml/NewRecipientPage.qml 2016-01-28 15:00:52 +0000 |
2441 | @@ -44,11 +44,29 @@ |
2442 | { |
2443 | multiRecipient.addRecipient(phoneNumber) |
2444 | multiRecipient.forceActiveFocus() |
2445 | - mainStack.pop() |
2446 | - } |
2447 | - |
2448 | - title: i18n.tr("Add recipient") |
2449 | - |
2450 | + mainStack.removePages(newRecipientPage) |
2451 | + } |
2452 | + |
2453 | + header: PageHeader { |
2454 | + id: pageHeader |
2455 | + |
2456 | + property alias leadingActions: leadingBar.actions |
2457 | + property alias trailingActions: trailingBar.actions |
2458 | + |
2459 | + title: i18n.tr("Add recipient") |
2460 | + leadingActionBar { |
2461 | + id: leadingBar |
2462 | + } |
2463 | + |
2464 | + trailingActionBar { |
2465 | + id: trailingBar |
2466 | + } |
2467 | + } |
2468 | + |
2469 | + Sections { |
2470 | + id: headerSections |
2471 | + model: [i18n.tr("All"), i18n.tr("Favorites")] |
2472 | + } |
2473 | TextField { |
2474 | id: searchField |
2475 | |
2476 | @@ -69,11 +87,10 @@ |
2477 | |
2478 | state: "default" |
2479 | states: [ |
2480 | - PageHeadState { |
2481 | + State { |
2482 | id: defaultState |
2483 | - |
2484 | name: "default" |
2485 | - actions: [ |
2486 | + property list<QtObject> trailingActions: [ |
2487 | Action { |
2488 | text: i18n.tr("Search") |
2489 | iconName: "search" |
2490 | @@ -84,10 +101,11 @@ |
2491 | } |
2492 | } |
2493 | ] |
2494 | + |
2495 | PropertyChanges { |
2496 | - target: newRecipientPage.head |
2497 | - actions: defaultState.actions |
2498 | - sections.model: [i18n.tr("All"), i18n.tr("Favorites")] |
2499 | + target: pageHeader |
2500 | + trailingActions: defaultState.trailingActions |
2501 | + extension: headerSections |
2502 | } |
2503 | PropertyChanges { |
2504 | target: searchField |
2505 | @@ -95,27 +113,34 @@ |
2506 | visible: false |
2507 | } |
2508 | }, |
2509 | - PageHeadState { |
2510 | + State { |
2511 | id: searchingState |
2512 | - |
2513 | name: "searching" |
2514 | - backAction: Action { |
2515 | - iconName: "back" |
2516 | - text: i18n.tr("Cancel") |
2517 | - onTriggered: { |
2518 | - newRecipientPage.forceActiveFocus() |
2519 | - newRecipientPage.state = "default" |
2520 | - newRecipientPage.head.sections.selectedIndex = 0 |
2521 | + property list<QtObject> leadingActions: [ |
2522 | + Action { |
2523 | + iconName: "back" |
2524 | + text: i18n.tr("Cancel") |
2525 | + onTriggered: { |
2526 | + newRecipientPage.forceActiveFocus() |
2527 | + newRecipientPage.state = "default" |
2528 | + headerSections.selectedIndex = 0 |
2529 | + } |
2530 | } |
2531 | - } |
2532 | + ] |
2533 | |
2534 | PropertyChanges { |
2535 | - target: newRecipientPage.head |
2536 | - backAction: searchingState.backAction |
2537 | + target: pageHeader |
2538 | + leadingActions: searchingState.leadingActions |
2539 | + trailingActions: [] |
2540 | contents: searchField |
2541 | } |
2542 | |
2543 | PropertyChanges { |
2544 | + target: headerSections |
2545 | + visible: false |
2546 | + } |
2547 | + |
2548 | + PropertyChanges { |
2549 | target: searchField |
2550 | text: "" |
2551 | visible: true |
2552 | @@ -139,12 +164,14 @@ |
2553 | filterTerm: searchField.text |
2554 | onContactClicked: { |
2555 | if (newRecipientPage.phoneToAdd != "") { |
2556 | - mainView.addPhoneToContact(contact, |
2557 | + mainView.addPhoneToContact(newRecipientPage, |
2558 | + contact, |
2559 | newRecipientPage.phoneToAdd, |
2560 | newRecipientPage, |
2561 | contactList.listModel) |
2562 | } else { |
2563 | - mainView.showContactDetails(contact, |
2564 | + mainView.showContactDetails(newRecipientPage, |
2565 | + contact, |
2566 | newRecipientPage, |
2567 | contactList.listModel) |
2568 | } |
2569 | @@ -152,24 +179,20 @@ |
2570 | |
2571 | onAddNewContactClicked: { |
2572 | var newContact = ContactsJS.createEmptyContact(newRecipientPage.phoneToAdd, newRecipientPage) |
2573 | - pageStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2574 | - { model: contactList.listModel, |
2575 | - contact: newContact, |
2576 | - initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), |
2577 | - contactListPage: newRecipientPage |
2578 | - }) |
2579 | + mainStack.addPageToCurrentColumn(newRecipientPage, |
2580 | + Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2581 | + { model: contactList.listModel, |
2582 | + contact: newContact, |
2583 | + initialFocusSection: (newRecipientPage.phoneToAdd != "" ? "phones" : "name"), |
2584 | + contactListPage: newRecipientPage }) |
2585 | } |
2586 | } |
2587 | |
2588 | - // WORKAROUND: This is necessary to make the header visible from a bottom edge page |
2589 | Component.onCompleted: { |
2590 | - parentPage.active = false |
2591 | if (QTCONTACTS_PRELOAD_VCARD !== "") { |
2592 | contactList.listModel.importContacts("file://" + QTCONTACTS_PRELOAD_VCARD) |
2593 | } |
2594 | } |
2595 | - Component.onDestruction: parentPage.active = true |
2596 | - |
2597 | onActiveChanged: { |
2598 | if (active && (state === "searching")) { |
2599 | searchField.forceActiveFocus() |
2600 | |
2601 | === modified file 'src/qml/messaging-app.qml' |
2602 | --- src/qml/messaging-app.qml 2016-01-28 15:00:52 +0000 |
2603 | +++ src/qml/messaging-app.qml 2016-01-28 15:00:52 +0000 |
2604 | @@ -39,6 +39,10 @@ |
2605 | } |
2606 | property QtObject account: defaultPhoneAccount() |
2607 | property bool applicationActive: Qt.application.active |
2608 | + property alias mainStack: layout |
2609 | + property bool dualPanel: mainStack.columns > 1 |
2610 | + property QtObject bottomEdge: null |
2611 | + property bool composingNewMessage: bottomEdge.status === BottomEdge.Committed |
2612 | |
2613 | activeFocusOnPress: false |
2614 | |
2615 | @@ -58,7 +62,7 @@ |
2616 | return null |
2617 | } |
2618 | |
2619 | - function showContactDetails(contact, contactListPage, contactsModel) { |
2620 | + function showContactDetails(currentPage, contact, contactListPage, contactsModel) { |
2621 | var initialProperties = { "contactListPage": contactListPage, |
2622 | "model": contactsModel} |
2623 | |
2624 | @@ -68,21 +72,24 @@ |
2625 | initialProperties['contact'] = contact |
2626 | } |
2627 | |
2628 | - mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2629 | - initialProperties) |
2630 | - } |
2631 | - |
2632 | - function addNewContact(phoneNumber, contactListPage) { |
2633 | - mainStack.push(Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2634 | - { "contactId": contactId, |
2635 | - "addPhoneToContact": phoneNumber, |
2636 | - "contactListPage": contactListPage }) |
2637 | - } |
2638 | - |
2639 | - function addPhoneToContact(contact, phoneNumber, contactListPage, contactsModel) { |
2640 | + mainStack.addPageToCurrentColumn(currentPage, |
2641 | + Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2642 | + initialProperties) |
2643 | + } |
2644 | + |
2645 | + function addNewContact(currentPage, phoneNumber, contactListPage) { |
2646 | + mainStack.addPageToCurrentColumn(currentPage, |
2647 | + Qt.resolvedUrl("MessagingContactEditorPage.qml"), |
2648 | + { "contactId": contactId, |
2649 | + "addPhoneToContact": phoneNumber, |
2650 | + "contactListPage": contactListPage }) |
2651 | + } |
2652 | + |
2653 | + function addPhoneToContact(currentPage, contact, phoneNumber, contactListPage, contactsModel) { |
2654 | if (contact === "") { |
2655 | - mainStack.push(Qt.resolvedUrl("NewRecipientPage.qml"), |
2656 | - { "phoneToAdd": phoneNumber }) |
2657 | + mainStack.addPageToCurrentColumn(currentPage, |
2658 | + Qt.resolvedUrl("NewRecipientPage.qml"), |
2659 | + { "phoneToAdd": phoneNumber }) |
2660 | } else { |
2661 | var initialProperties = { "addPhoneToContact": phoneNumber, |
2662 | "contactListPage": contactListPage, |
2663 | @@ -92,8 +99,9 @@ |
2664 | } else { |
2665 | initialProperties['contact'] = contact |
2666 | } |
2667 | - mainStack.push(Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2668 | - initialProperties) |
2669 | + mainStack.addPageToCurrentColumn(currentPage, |
2670 | + Qt.resolvedUrl("MessagingContactViewPage.qml"), |
2671 | + initialProperties) |
2672 | } |
2673 | } |
2674 | |
2675 | @@ -119,6 +127,10 @@ |
2676 | threadModel.removeThreads(threads); |
2677 | } |
2678 | |
2679 | + function showBottomEdgePage(properties) { |
2680 | + bottomEdge.commitWithProperties(properties) |
2681 | + } |
2682 | + |
2683 | Connections { |
2684 | target: telepathyHelper |
2685 | // restore default bindings if any system settings changed |
2686 | @@ -141,13 +153,14 @@ |
2687 | Component.onCompleted: { |
2688 | i18n.domain = "messaging-app" |
2689 | i18n.bindtextdomain("messaging-app", i18nDirectory) |
2690 | + emptyStack() |
2691 | } |
2692 | |
2693 | Connections { |
2694 | target: telepathyHelper |
2695 | onSetupReady: { |
2696 | if (multiplePhoneAccounts && !telepathyHelper.defaultMessagingAccount && |
2697 | - !settings.mainViewIgnoreFirstTimeDialog && mainStack.depth === 1) { |
2698 | + !settings.mainViewIgnoreFirstTimeDialog && mainPage.displayedThreadIndex < 0) { |
2699 | PopupUtils.open(Qt.createComponent("Dialogs/NoDefaultSIMCardDialog.qml").createObject(mainView)) |
2700 | } |
2701 | } |
2702 | @@ -187,7 +200,7 @@ |
2703 | var properties = {} |
2704 | emptyStack() |
2705 | properties["sharedAttachmentsTransfer"] = transfer |
2706 | - mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml"), properties) |
2707 | + mainView.showBottomEdgePage(properties) |
2708 | } |
2709 | } |
2710 | |
2711 | @@ -211,15 +224,20 @@ |
2712 | } |
2713 | |
2714 | function emptyStack() { |
2715 | - while (mainStack.depth !== 1 && mainStack.depth !== 0) { |
2716 | - mainStack.pop() |
2717 | + mainStack.removePages(mainPage) |
2718 | + showEmptyState() |
2719 | + } |
2720 | + |
2721 | + function showEmptyState() { |
2722 | + if (mainStack.columns > 1) { |
2723 | + layout.addPageToNextColumn(mainStack.primaryPage, emptyStatePageComponent.createObject(null)) |
2724 | } |
2725 | } |
2726 | |
2727 | function startNewMessage() { |
2728 | var properties = {} |
2729 | emptyStack() |
2730 | - mainStack.currentPage.showBottomEdgePage(Qt.resolvedUrl("Messages.qml")) |
2731 | + mainView.showBottomEdgePage(properties) |
2732 | } |
2733 | |
2734 | function startChat(identifiers, text, accountId) { |
2735 | @@ -261,9 +279,24 @@ |
2736 | if (typeof(accountId)!=='undefined') { |
2737 | properties["accountId"] = accountId |
2738 | } |
2739 | + |
2740 | emptyStack() |
2741 | - mainStack.push(Qt.resolvedUrl("Messages.qml"), properties) |
2742 | - } |
2743 | + mainStack.addPageToNextColumn(mainPage, messagesWithBottomEdge, properties) |
2744 | + } |
2745 | + |
2746 | + InputInfo { |
2747 | + id: inputInfo |
2748 | + } |
2749 | + |
2750 | + // WORKAROUND: Due the missing feature on SDK, they can not detect if |
2751 | + // there is a mouse attached to device or not. And this will cause the |
2752 | + // bootom edge component to not work correct on desktop. |
2753 | + Binding { |
2754 | + target: QuickUtils |
2755 | + property: "mouseAttached" |
2756 | + value: inputInfo.hasMouse |
2757 | + } |
2758 | + |
2759 | |
2760 | Connections { |
2761 | target: UriHandler |
2762 | @@ -274,11 +307,57 @@ |
2763 | } |
2764 | } |
2765 | |
2766 | - |
2767 | - PageStack { |
2768 | - id: mainStack |
2769 | - |
2770 | - objectName: "mainStack" |
2771 | - Component.onCompleted: mainStack.push(Qt.resolvedUrl("MainPage.qml")) |
2772 | + Component { |
2773 | + id: messagesWithBottomEdge |
2774 | + |
2775 | + Messages { |
2776 | + id: messages |
2777 | + Loader { |
2778 | + id: messagesBottomEdgeLoader |
2779 | + active: mainView.dualPanel |
2780 | + sourceComponent: MessagingBottomEdge { |
2781 | + id: messagesBottomEdge |
2782 | + parent: messages |
2783 | + hint.text: "" |
2784 | + hint.height: 0 |
2785 | + } |
2786 | + } |
2787 | + } |
2788 | + } |
2789 | + |
2790 | + Component { |
2791 | + id: emptyStatePageComponent |
2792 | + Page { |
2793 | + id: emptyStatePage |
2794 | + |
2795 | + EmptyState { |
2796 | + labelVisible: mainPage.isEmpty |
2797 | + } |
2798 | + |
2799 | + header: PageHeader { } |
2800 | + |
2801 | + Loader { |
2802 | + id: bottomEdgeLoader |
2803 | + sourceComponent: MessagingBottomEdge { |
2804 | + parent: emptyStatePage |
2805 | + } |
2806 | + } |
2807 | + } |
2808 | + } |
2809 | + |
2810 | + AdaptivePageLayout { |
2811 | + id: layout |
2812 | + anchors.fill: parent |
2813 | + primaryPage: MainPage { |
2814 | + id: mainPage |
2815 | + } |
2816 | + |
2817 | + onColumnsChanged: { |
2818 | + // we only have things to do here in case no thread is selected |
2819 | + if (mainPage.displayedThreadIndex < 0) { |
2820 | + layout.removePages(mainPage) |
2821 | + showEmptyState() |
2822 | + } |
2823 | + } |
2824 | } |
2825 | } |
2826 | |
2827 | === modified file 'tests/autopilot/messaging_app/emulators.py' |
2828 | --- tests/autopilot/messaging_app/emulators.py 2015-11-04 19:57:45 +0000 |
2829 | +++ tests/autopilot/messaging_app/emulators.py 2016-01-28 15:00:52 +0000 |
2830 | @@ -78,14 +78,14 @@ |
2831 | |
2832 | def click_header_action(self, action): |
2833 | """Click the action 'action' on the header""" |
2834 | - self.get_header().click_action_button(action) |
2835 | + action = self.wait_select_single(objectName='%s_button' % action) |
2836 | + self.pointing_device.click_object(action) |
2837 | |
2838 | # messages page |
2839 | def get_messages_page(self): |
2840 | """Return messages with objectName messagesPage""" |
2841 | |
2842 | - return self.wait_select_single("Messages", objectName="messagesPage", |
2843 | - active=True) |
2844 | + return self.wait_select_single("Messages", objectName="messagesPage") |
2845 | |
2846 | def get_newmessage_textfield(self): |
2847 | """Return TextField with objectName newPhoneNumberField""" |
2848 | @@ -116,40 +116,12 @@ |
2849 | |
2850 | return self.get_messages_page().select_single(objectName='sendButton') |
2851 | |
2852 | - def get_toolbar_back_button(self): |
2853 | - """Return toolbar button with objectName back_toolbar_button""" |
2854 | - |
2855 | - return self.select_single( |
2856 | - objectName='back_toolbar_button' |
2857 | - ) |
2858 | - |
2859 | - def get_toolbar_select_messages_button(self): |
2860 | - """Return toolbar button with objectName selectMessagesButton""" |
2861 | - |
2862 | - return self.select_single( |
2863 | - objectName='selectMessagesButton' |
2864 | - ) |
2865 | - |
2866 | def get_contact_list_view(self): |
2867 | """Returns the ContactListView object""" |
2868 | return self.select_single( |
2869 | objectName='newRecipientList' |
2870 | ) |
2871 | |
2872 | - def get_toolbar_contact_profile_button(self): |
2873 | - """Return toolbar button with objectName contactProfileButton""" |
2874 | - |
2875 | - return self.select_single( |
2876 | - objectName='contactProfileButton' |
2877 | - ) |
2878 | - |
2879 | - def get_toolbar_contact_call_button(self): |
2880 | - """Return toolbar button with objectName contactCallButton""" |
2881 | - |
2882 | - return self.select_single( |
2883 | - objectName='contactCallButton' |
2884 | - ) |
2885 | - |
2886 | def get_dialog_buttons(self, visible=True): |
2887 | """Return DialogButtons |
2888 | |
2889 | @@ -213,7 +185,7 @@ |
2890 | |
2891 | def start_new_message(self): |
2892 | """Reveal the bottom edge page to start composing a new message""" |
2893 | - self.get_main_page().reveal_bottom_edge_page() |
2894 | + self.reveal_bottom_edge_page() |
2895 | |
2896 | def enable_messages_selection_mode(self): |
2897 | """Enable the selection mode on the messages page by pressing and |
2898 | @@ -338,38 +310,30 @@ |
2899 | |
2900 | def open_settings_page(self): |
2901 | self.click_threads_header_settings() |
2902 | - return self.wait_select_single(SettingsPage) |
2903 | + settings_page = self.wait_select_single(SettingsPage) |
2904 | + settings_page.active.waitFor(True) |
2905 | + return settings_page |
2906 | |
2907 | def get_swipe_item_demo(self): |
2908 | return self.wait_select_single( |
2909 | 'SwipeItemDemo', objectName='swipeItemDemo', parentActive=True) |
2910 | |
2911 | - |
2912 | -class PageWithBottomEdge(MainView): |
2913 | - """An emulator class that makes it easy to interact with the bottom edge |
2914 | - swipe page""" |
2915 | - def __init__(self, *args): |
2916 | - super(PageWithBottomEdge, self).__init__(*args) |
2917 | - |
2918 | def reveal_bottom_edge_page(self): |
2919 | """Bring the bottom edge page to the screen""" |
2920 | - self.bottomEdgePageLoaded.wait_for(True) |
2921 | try: |
2922 | - action_item = self.wait_select_single(objectName='bottomEdgeTip') |
2923 | - start_x = (action_item.globalRect.x + |
2924 | - (action_item.globalRect.width * 0.5)) |
2925 | - start_y = (action_item.globalRect.y + |
2926 | - (action_item.height * 0.5)) |
2927 | + start_x = (self.globalRect.x + |
2928 | + (self.globalRect.width * 0.5)) |
2929 | + start_y = (self.globalRect.y + self.height) |
2930 | stop_y = start_y - (self.height * 0.7) |
2931 | self.pointing_device.drag(start_x, start_y, |
2932 | start_x, stop_y, rate=2) |
2933 | - self.isReady.wait_for(True) |
2934 | + self.composingNewMessage.wait_for(True) |
2935 | except StateNotFoundError: |
2936 | logger.error('BottomEdge element not found.') |
2937 | raise |
2938 | |
2939 | |
2940 | -class MainPage(PageWithBottomEdge): |
2941 | +class MainPage(MainView): |
2942 | """Autopilot helper for the Main Page.""" |
2943 | |
2944 | def get_thread_count(self): |
2945 | |
2946 | === modified file 'tests/qml/CMakeLists.txt' |
2947 | --- tests/qml/CMakeLists.txt 2016-01-28 15:00:52 +0000 |
2948 | +++ tests/qml/CMakeLists.txt 2016-01-28 15:00:52 +0000 |
2949 | @@ -36,7 +36,8 @@ |
2950 | |
2951 | add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} |
2952 | -input ${CMAKE_CURRENT_SOURCE_DIR} |
2953 | - -import ${CMAKE_BINARY_DIR}/src) |
2954 | + -import ${CMAKE_BINARY_DIR}/src |
2955 | + -import ${UNITY8_QML_PATH}) |
2956 | |
2957 | # make qml files visible in QtCreator |
2958 | file(GLOB_RECURSE NON_COMPILED_FILES *.qml) |
2959 | |
2960 | === modified file 'tests/qml/tst_MessagesView.qml' |
2961 | --- tests/qml/tst_MessagesView.qml 2015-11-19 00:34:45 +0000 |
2962 | +++ tests/qml/tst_MessagesView.qml 2016-01-28 15:00:52 +0000 |
2963 | @@ -129,16 +129,21 @@ |
2964 | var senderId = "1234567" |
2965 | var stack = findChild(mainViewLoader, "mainStack") |
2966 | tryCompare(mainViewLoader.item, 'applicationActive', true) |
2967 | - tryCompare(stack, 'depth', 1) |
2968 | // if messaging-app has no account set, it will not try to get the thread from history |
2969 | // and instead will generate the list of participants, take advantage of that |
2970 | var account = mainViewLoader.item.account |
2971 | mainViewLoader.item.account = null |
2972 | mainViewLoader.item.startChat(senderId, "") |
2973 | mainViewLoader.item.account = account |
2974 | - tryCompare(stack, 'depth', 2) |
2975 | - mainViewLoader.item.applicationActive = false |
2976 | - var messageList = findChild(mainViewLoader, "messageList") |
2977 | + var messageList |
2978 | + while (true) { |
2979 | + messageList = findChild(mainViewLoader, "messageList") |
2980 | + if (messageList) { |
2981 | + break |
2982 | + } |
2983 | + wait(200) |
2984 | + } |
2985 | + |
2986 | messageList.listModel = messagesModel |
2987 | tryCompare(messageList, 'count', 2) |
2988 | compare(messageAcknowledgeSpy.count, 0) |
FAILED: Continuous integration, rev:449 jenkins. qa.ubuntu. com/job/ messaging- app-ci/ 685/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 4994/console jenkins. qa.ubuntu. com/job/ messaging- app-vivid- i386-ci/ 198/console jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 4991/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/messaging- app-ci/ 685/rebuild
http://