1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implementation for share player. |
|
15 * |
|
16 */ |
|
17 |
|
18 #ifdef SHARE_FUNC_ENABLED |
|
19 |
|
20 #include "mpdetailssharedialog.h" |
|
21 #include "mpsharedata.h" |
|
22 #include "mpsongdata.h" |
|
23 #include "mptrace.h" |
|
24 #include <QObject> |
|
25 #include <QGraphicsWebView> |
|
26 #include <QGraphicsScene> |
|
27 #include <QWebPage> |
|
28 #include <QWebFrame> |
|
29 #include <QNetworkAccessManager> |
|
30 #include <QNetworkDiskCache> |
|
31 #include <QNetworkReply> |
|
32 #include <QDesktopServices> |
|
33 #include <QNetworkProxyFactory> |
|
34 #include <QTimer> |
|
35 #include <hbmessagebox.h> |
|
36 #include <QFile> |
|
37 #include <QTextStream> |
|
38 #include <qsysteminfo.h> |
|
39 #include <hbmainwindow.h> |
|
40 #include <hbstyleloader.h> |
|
41 #include <hbprogressdialog.h> |
|
42 |
|
43 QTM_USE_NAMESPACE |
|
44 |
|
45 // SHARE_INDEX_FILE defines where the index.html file is loaded from. |
|
46 #define SHARE_INDEX_URL "qrc:///shareview/index.html" |
|
47 |
|
48 #ifdef Q_OS_SYMBIAN |
|
49 // Symbian target. |
|
50 #ifdef SHARE_PLAYER_RND |
|
51 // For R&D testing, index.html may be loaded from E: drive. |
|
52 // User must manually place index.html in the correct location. |
|
53 // If the RND file does not exist, then SHARE_INDEX_URL will be used. |
|
54 #define RND_SHARE_INDEX_URL "file:///f:/index.html" |
|
55 #define RND_SHARE_INDEX_FILE "f:\\index.html" |
|
56 #define RND_OVI_LOGIN_FILE "f:\\ovicredentials.txt" |
|
57 #endif |
|
58 #else |
|
59 // Assume Windows target. |
|
60 #define RND_SHARE_INDEX_URL "file:///c:/temp/index.html" |
|
61 #define RND_SHARE_INDEX_FILE "c:\\temp\\index.html" |
|
62 #define RND_OVI_LOGIN_FILE "c:\\temp\\ovicredentials.txt" |
|
63 |
|
64 #endif |
|
65 |
|
66 // Default language in case QSystemInfo does not work. |
|
67 #define DEFAULT_LANGUAGE "en-US" |
|
68 |
|
69 // Default error message. |
|
70 #define ERROR_MESSAGE "An error occured. Sharing is not currently available" |
|
71 |
|
72 |
|
73 /*! |
|
74 MpNetworkAccessManager allows local caching of publishing player files |
|
75 in order to minimize network traffic. |
|
76 The files will be cached to the private directory of the application, |
|
77 i.e. in the music player's case, this is C:\Private\10207C62\Cache. |
|
78 */ |
|
79 class MpNetworkAccessManager : public QNetworkAccessManager |
|
80 { |
|
81 public: |
|
82 MpNetworkAccessManager( QObject* parent = 0 ) |
|
83 : QNetworkAccessManager( parent ) |
|
84 { |
|
85 proxyFactory()->setUseSystemConfiguration( true ); |
|
86 QNetworkDiskCache* diskCache = new QNetworkDiskCache( this ); |
|
87 QString location = QDesktopServices::storageLocation( QDesktopServices::CacheLocation ); |
|
88 diskCache->setCacheDirectory( location ); |
|
89 setCache( diskCache ); |
|
90 } |
|
91 |
|
92 private: |
|
93 QNetworkReply* createRequest( Operation op, |
|
94 const QNetworkRequest &request, |
|
95 QIODevice* outgoingData = 0 ) |
|
96 { |
|
97 TX_ENTRY |
|
98 TX_LOG_ARGS( "share: createRequest URL=" << request.url().toString() ) |
|
99 |
|
100 QVariant val = request.attribute( QNetworkRequest::CacheLoadControlAttribute ); |
|
101 |
|
102 // Change the cache load control attrbute! |
|
103 QNetworkRequest req = request; |
|
104 req.setAttribute( QNetworkRequest::CacheLoadControlAttribute, |
|
105 QVariant( QNetworkRequest::PreferCache ) ); |
|
106 QNetworkReply* result = QNetworkAccessManager::createRequest( op, req, outgoingData ); |
|
107 TX_EXIT |
|
108 return result; |
|
109 } |
|
110 }; |
|
111 |
|
112 /*! |
|
113 MpShareWebView derives from QGraphicsWebView in order to override it's |
|
114 contextMenuEvent method to prevent the background context menu from |
|
115 being displayed when user makes long click in the web view. |
|
116 */ |
|
117 class MpShareWebView : public QGraphicsWebView |
|
118 { |
|
119 public: |
|
120 MpShareWebView( QGraphicsItem * parent = 0 ) |
|
121 : QGraphicsWebView( parent ) |
|
122 { |
|
123 settings()->setAttribute( QWebSettings::LocalContentCanAccessRemoteUrls, true ); |
|
124 settings()->setAttribute( QWebSettings::LocalStorageDatabaseEnabled, true ); |
|
125 settings()->enablePersistentStorage(); |
|
126 } |
|
127 |
|
128 protected: |
|
129 void contextMenuEvent( QGraphicsSceneContextMenuEvent* /*ev*/ ) // Override QGraphicsWebView::contextMenuEvent |
|
130 { |
|
131 // Fix to prevent "Stop" and "Reload" buttons in page background. |
|
132 // Do not respond to the contextMenuEvent. |
|
133 } |
|
134 }; |
|
135 |
|
136 |
|
137 /*! |
|
138 Constructor. |
|
139 */ |
|
140 MpDetailsShareDialog::MpDetailsShareDialog() |
|
141 : mShareWebView( 0 ), |
|
142 mShareNetAccMan( 0 ), |
|
143 mProgressbar ( 0 ), |
|
144 mIsInitialized( false ) |
|
145 { |
|
146 // DeleteOnClose attribute prevents crash when user presses Cancel |
|
147 // before publishing player is fully loaded. |
|
148 setAttribute( Qt::WA_DeleteOnClose, true ); |
|
149 } |
|
150 |
|
151 /*! |
|
152 Initialize the share dialog. |
|
153 When fully initialized we set our mIsInitialized flag to true. |
|
154 Our isInitialized() method can be called to determine whether |
|
155 initialization was successful. |
|
156 */ |
|
157 void MpDetailsShareDialog::initialize( MpSongData* aSongData, const QString& aUnknownTr ) |
|
158 { |
|
159 TX_ENTRY |
|
160 if ( !initUser() ) |
|
161 { |
|
162 emit closeShareDialog(); |
|
163 return; |
|
164 } |
|
165 initShareData( aSongData, aUnknownTr ); |
|
166 initLanguage(); |
|
167 initNetworkAccessManager(); |
|
168 initWebView(); |
|
169 initSignalSlots(); |
|
170 |
|
171 setDismissPolicy( HbDialog::NoDismiss ); |
|
172 |
|
173 // No timeout needed for the dialog. |
|
174 setTimeout( HbPopup::NoTimeout ); |
|
175 |
|
176 #ifdef SHARE_PLAYER_RND |
|
177 // Test whether the RND file exists. |
|
178 QFile file( RND_SHARE_INDEX_FILE ); |
|
179 if ( file.exists() ) |
|
180 { |
|
181 // Load the RND URL from the specified location to the web view. |
|
182 TX_LOG_ARGS( "share: Use RND index.html file " << RND_SHARE_INDEX_FILE ) |
|
183 mShareWebView->load( QUrl( RND_SHARE_INDEX_URL ) ); |
|
184 } |
|
185 else |
|
186 #endif |
|
187 { |
|
188 // Load the production URL from the application resources to the web view. |
|
189 TX_LOG_ARGS( "share: Use QRC index.html file " << SHARE_INDEX_URL ) |
|
190 mShareWebView->load( QUrl( SHARE_INDEX_URL ) ); |
|
191 } |
|
192 |
|
193 // Flag that the dialog is now fully initialized. |
|
194 mIsInitialized = true; |
|
195 |
|
196 // Show progress dialog in .3 second if loading not finished. |
|
197 QTimer::singleShot(300, this, SLOT(showProgressDialog())); |
|
198 |
|
199 TX_EXIT |
|
200 } |
|
201 |
|
202 /*! |
|
203 Destructor. |
|
204 */ |
|
205 MpDetailsShareDialog::~MpDetailsShareDialog() |
|
206 { |
|
207 TX_ENTRY |
|
208 if ( mShareData.songData() ) |
|
209 { |
|
210 // Ensure that we remove the temporary album art file when we close the dialog. |
|
211 // TODO this should be removed when base64 issue is solved. |
|
212 TX_LOG_ARGS( "share: remove album art file" ) |
|
213 mShareData.songData()->removeAlbumArtFile(); |
|
214 } |
|
215 logoutPlayer(); |
|
216 // Probably mShareNetAccMan should not be deleted but qt documentation |
|
217 // does not indicate whether QWebPage takes ownership of the object or not. |
|
218 // See http://doc.qt.nokia.com/4.6/qwebpage.html |
|
219 //delete mShareNetAccMan; |
|
220 TX_EXIT |
|
221 } |
|
222 |
|
223 /*! |
|
224 Initialize share data. |
|
225 */ |
|
226 void MpDetailsShareDialog::initShareData( MpSongData* aSongData, const QString& aUnknownTr ) |
|
227 { |
|
228 TX_ENTRY |
|
229 // Set information for the share data. |
|
230 mShareData.setOwner( this ); |
|
231 mShareData.setSongData( aSongData ); |
|
232 mShareData.setErrorMessage( tr( ERROR_MESSAGE ) ); |
|
233 mShareData.setUnknownTr( aUnknownTr ); |
|
234 TX_EXIT |
|
235 } |
|
236 |
|
237 /*! |
|
238 Initialize language. |
|
239 Language string is formatted like "en-US", where "en" is the ISO-639-1 language code, |
|
240 and "US" is the ISO-3166-1 country code. |
|
241 We use the QT Mobility API (systeminfo) to obtain the settings from the device. |
|
242 In the event that we cannot construct the QSystemInfo variable then we will fallback |
|
243 to some DEFAULT_LANGUAGE setting. |
|
244 */ |
|
245 void MpDetailsShareDialog::initLanguage() |
|
246 { |
|
247 TX_ENTRY |
|
248 // Set language string, example "en-US". |
|
249 QString language; |
|
250 QSystemInfo* sysInfo = new QSystemInfo( this ); |
|
251 if ( sysInfo ) |
|
252 { |
|
253 language += sysInfo->currentLanguage(); // ISO-639-1 language code. |
|
254 language += "-"; |
|
255 language += sysInfo->currentCountryCode(); // ISO-3166-1 country code. |
|
256 delete sysInfo; |
|
257 } |
|
258 else |
|
259 { |
|
260 // Fallback to the default language. |
|
261 language = DEFAULT_LANGUAGE; |
|
262 } |
|
263 TX_LOG_ARGS( "share: language '" << language << "'" ) |
|
264 mShareData.setLanguage( language ); |
|
265 TX_EXIT |
|
266 } |
|
267 |
|
268 /*! |
|
269 Initialize network access manager. |
|
270 */ |
|
271 void MpDetailsShareDialog::initNetworkAccessManager() |
|
272 { |
|
273 TX_ENTRY |
|
274 // Make our own network access manager to allow file retrieval from local cache, |
|
275 // since configuration for the default network access manager seems to be |
|
276 // to always redownload from network. |
|
277 if ( !mShareNetAccMan ) |
|
278 { |
|
279 TX_LOG_ARGS( "share: construct network access manager" ) |
|
280 mShareNetAccMan = new MpNetworkAccessManager( this ); |
|
281 } |
|
282 TX_EXIT |
|
283 } |
|
284 |
|
285 /*! |
|
286 Initialize the web view. |
|
287 */ |
|
288 void MpDetailsShareDialog::initWebView() |
|
289 { |
|
290 TX_ENTRY |
|
291 if ( !mShareWebView ) |
|
292 { |
|
293 TX_LOG_ARGS( "share: construct share web view" ) |
|
294 mShareWebView = new MpShareWebView( this ); |
|
295 mShareWebView->page()->setNetworkAccessManager( mShareNetAccMan ); |
|
296 mShareWebView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); |
|
297 mShareWebView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); |
|
298 setContentWidget( mShareWebView ); |
|
299 addContext(); |
|
300 mProgressbar = new HbProgressDialog(HbProgressDialog::WaitDialog); |
|
301 mProgressbar->setText(tr("Loading")); |
|
302 } |
|
303 TX_EXIT |
|
304 } |
|
305 |
|
306 /*! |
|
307 Initialize signals and slots. |
|
308 */ |
|
309 void MpDetailsShareDialog::initSignalSlots() |
|
310 { |
|
311 TX_ENTRY |
|
312 // Connect various signals to slots for networking. |
|
313 connect( mShareNetAccMan, SIGNAL( sslErrors( QNetworkReply*, const QList< QSslError >& ) ), |
|
314 this, SLOT( handleRequestSSLErrors( QNetworkReply*, const QList< QSslError >& ) ) ); |
|
315 connect( mShareNetAccMan, SIGNAL( finished( QNetworkReply* ) ), this, SLOT( handleRequestFinished( QNetworkReply* ) ) ); |
|
316 |
|
317 // Connect various signals to slots for interface to webview. |
|
318 connect( mShareWebView, SIGNAL( loadFinished( bool ) ), SLOT( onIndexLoad( bool ) ) ); |
|
319 connect( mShareWebView->page()->mainFrame(), SIGNAL( javaScriptWindowObjectCleared() ), this, SLOT( addContext() ) ); |
|
320 connect( mShareWebView->page(), SIGNAL( windowCloseRequested() ), this, SIGNAL( closeShareDialog() ) ); |
|
321 connect( mProgressbar, SIGNAL(cancelled()), this, SIGNAL( closeShareDialog() ) ); |
|
322 TX_EXIT |
|
323 } |
|
324 |
|
325 /*! |
|
326 initUser is used as temporary solution until Single Sign On is implemented in platform. |
|
327 */ |
|
328 bool MpDetailsShareDialog::initUser() |
|
329 { |
|
330 TX_ENTRY |
|
331 bool result = false; |
|
332 #ifdef SHARE_PLAYER_RND |
|
333 // ovicredentials.txt is used as temporary solution until Single Sign On is implemented in platform. |
|
334 QFile file( RND_OVI_LOGIN_FILE ); |
|
335 if ( !file.open( QFile::ReadOnly ) ) |
|
336 { |
|
337 // There must be e:ovicredentials.txt in the filesystem but don't show error dialog |
|
338 // otherwise it will appear as soon as detailsview is created. |
|
339 result = false; |
|
340 } |
|
341 else |
|
342 { |
|
343 QTextStream stream ( &file ); |
|
344 QString strCred = stream.readLine( 0 ); |
|
345 file.close(); |
|
346 QStringList slCred = strCred.split( ":" ); |
|
347 if ( slCred.length() > 1 ) |
|
348 { |
|
349 mShareData.setUsername( slCred[ 0 ] ); |
|
350 mShareData.setPassword( slCred[ 1 ] ); |
|
351 result = true; |
|
352 } |
|
353 else |
|
354 { |
|
355 errorHandler( "share", QString( RND_OVI_LOGIN_FILE ) + " username:password expected" ); |
|
356 } |
|
357 } |
|
358 TX_LOG_ARGS( "share: credentials " << mShareData.username() << " / " << mShareData.password() ) |
|
359 #else |
|
360 // TODO: Single Sign On stuff. |
|
361 #endif // SHARE_PLAYER_RND |
|
362 TX_EXIT |
|
363 return result; |
|
364 } |
|
365 |
|
366 /*! |
|
367 Returns true if the dialog has been fully initialized. |
|
368 */ |
|
369 bool MpDetailsShareDialog::isInitialized() const |
|
370 { |
|
371 return mIsInitialized; |
|
372 } |
|
373 |
|
374 /*! |
|
375 Attempt to cache the publishing player files from internet |
|
376 to improve user experience for first-time use. |
|
377 If the files are already in the cache and have not expired, |
|
378 then this should not do anything. |
|
379 */ |
|
380 void MpDetailsShareDialog::cachePublishingPlayerFiles() |
|
381 { |
|
382 TX_ENTRY |
|
383 // We need the network access manager, so make sure it is initialized. |
|
384 if ( !mShareNetAccMan ) |
|
385 { |
|
386 initNetworkAccessManager(); |
|
387 } |
|
388 // Attempt to get the required publishing player files from the net in advance. |
|
389 // We don't listen to any signal that the download succeeded or failed |
|
390 // since we will let it silently fail at this stage. |
|
391 // These URLs are also included in index.html resource, and the two must |
|
392 // be kept the same. |
|
393 mShareNetAccMan->get( QNetworkRequest( QUrl( |
|
394 "http://hf.ci.wipsl.com/PleiXXPTsup/noheva-be/css/ovi.player.share.ui.css" ) ) ); |
|
395 mShareNetAccMan->get( QNetworkRequest( QUrl( |
|
396 "http://hf.ci.wipsl.com/PleiXXPTsup/noheva-be/js/publishplayer.js" ) ) ); |
|
397 TX_EXIT |
|
398 } |
|
399 |
|
400 /*! |
|
401 Release resources from share player. |
|
402 */ |
|
403 void MpDetailsShareDialog::logoutPlayer() |
|
404 { |
|
405 TX_ENTRY |
|
406 if ( mShareWebView ) |
|
407 { |
|
408 mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.teardown();" ); |
|
409 } |
|
410 TX_EXIT |
|
411 } |
|
412 |
|
413 /*! |
|
414 Adds the shared data context to the javascript of the loaded page. |
|
415 */ |
|
416 void MpDetailsShareDialog::addContext() |
|
417 { |
|
418 TX_ENTRY |
|
419 if ( mShareWebView ) |
|
420 { |
|
421 mShareWebView->page()->mainFrame()->addToJavaScriptWindowObject( "context", &mShareData ); |
|
422 } |
|
423 TX_EXIT |
|
424 } |
|
425 |
|
426 /*! |
|
427 Updates the shared data context in the javascript of the loaded page. |
|
428 */ |
|
429 void MpDetailsShareDialog::updateSharedData() |
|
430 { |
|
431 TX_ENTRY |
|
432 if ( mShareWebView ) |
|
433 { |
|
434 // We don't need to call updateContextArea when the track URL has been updated. |
|
435 //mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.updateContextArea();" ); |
|
436 mShareWebView->page()->mainFrame()->evaluateJavaScript( "music.updateMetadata();" ); |
|
437 } |
|
438 TX_EXIT |
|
439 } |
|
440 |
|
441 /*! |
|
442 Slot to call when index.html loading completes. |
|
443 */ |
|
444 void MpDetailsShareDialog::onIndexLoad( bool aOk ) |
|
445 { |
|
446 TX_ENTRY |
|
447 if ( !aOk ) |
|
448 { |
|
449 // Close the popup window, failed to load index.html. |
|
450 // This is pretty serious and most likely unrecoverable error. |
|
451 // Only thing we can do really is to close the share player |
|
452 // dialog - TODO do we need to show any error message to user? |
|
453 TX_LOG_ARGS( "share: failed to load index.html" ) |
|
454 emit closeShareDialog(); |
|
455 } |
|
456 TX_EXIT |
|
457 } |
|
458 |
|
459 /*! |
|
460 Slot to call for debug output. |
|
461 */ |
|
462 void MpDetailsShareDialog::debugJs( QString s ) |
|
463 { |
|
464 TX_ENTRY |
|
465 Q_UNUSED(s); |
|
466 |
|
467 TX_EXIT |
|
468 } |
|
469 |
|
470 /*! |
|
471 Slot to call for displaying an error message to the user. |
|
472 */ |
|
473 void MpDetailsShareDialog::errorHandler( QString aError, QString aMessage ) |
|
474 { |
|
475 TX_ENTRY |
|
476 // If error argument ends with "_SUCCESS", then this should be an info message. |
|
477 // If error argument ends with "_FAILED" or something else, then this should be a warning message. |
|
478 TX_LOG_ARGS( "share: errorHandler: " << aError << ": " << aMessage ) |
|
479 if ( aError.endsWith( "_SUCCESS" ) ) |
|
480 { |
|
481 // TODO this method seems to be deprecated? |
|
482 HbMessageBox::information( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( aMessage ) ); // For week16 hbwidgets |
|
483 } |
|
484 else |
|
485 { |
|
486 // TODO this method seems to be deprecated? |
|
487 HbMessageBox::warning( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( aMessage ) ); // For week16 hbwidgets |
|
488 // HbMessageBox::launchWarningMessageBox( tr( "%1" ).arg( aError ) + ": " + tr( "%1" ).arg( message ) ); // For week12 hbwidgets |
|
489 } |
|
490 TX_EXIT |
|
491 } |
|
492 |
|
493 /*! |
|
494 Slot to call to clear the web view cache. |
|
495 */ |
|
496 void MpDetailsShareDialog::clearCache() |
|
497 { |
|
498 TX_ENTRY |
|
499 QAbstractNetworkCache* cache = mShareNetAccMan ? mShareNetAccMan->cache() : NULL; |
|
500 if ( cache ) |
|
501 { |
|
502 TX_LOG_ARGS( "share: clearCache: clearing cache" ) |
|
503 cache->clear(); |
|
504 #ifdef SHARE_PLAYER_RND |
|
505 errorHandler( "Cache", "Cleared cache!" ); |
|
506 #endif |
|
507 } |
|
508 else |
|
509 { |
|
510 TX_LOG_ARGS( "share: clearCache: unable to clear cache" ) |
|
511 #ifdef SHARE_PLAYER_RND |
|
512 errorHandler( "Cache", "Could not clear cache!" ); |
|
513 #endif |
|
514 } |
|
515 TX_EXIT |
|
516 } |
|
517 |
|
518 /*! |
|
519 Slot to show the publishing window after html elements are created in javascript. |
|
520 */ |
|
521 void MpDetailsShareDialog::showWindow() |
|
522 { |
|
523 TX_ENTRY |
|
524 if (mProgressbar) |
|
525 mProgressbar->close(); |
|
526 show(); |
|
527 TX_EXIT |
|
528 } |
|
529 |
|
530 /*! |
|
531 Slot to show progress dialog if the publishing window is not loaded in .3 sec. |
|
532 */ |
|
533 void MpDetailsShareDialog::showProgressDialog() |
|
534 { |
|
535 TX_ENTRY |
|
536 if (!isVisible()) |
|
537 mProgressbar->show(); |
|
538 TX_EXIT |
|
539 } |
|
540 |
|
541 /*! |
|
542 Slot to SSL errors in network request. We will ignore any errors. |
|
543 */ |
|
544 void MpDetailsShareDialog::handleRequestSSLErrors( QNetworkReply* aReply, const QList< QSslError >& aErrors ) |
|
545 { |
|
546 TX_ENTRY |
|
547 aReply->ignoreSslErrors(); |
|
548 for( int i = 0; i < aErrors.count(); i++ ) |
|
549 { |
|
550 TX_LOG_ARGS( "SSL error " << aErrors.at( i ).errorString() ); |
|
551 } |
|
552 TX_EXIT |
|
553 } |
|
554 |
|
555 /*! |
|
556 Slot to handle network request completion. |
|
557 */ |
|
558 void MpDetailsShareDialog::handleRequestFinished( QNetworkReply* aReply ) |
|
559 { |
|
560 TX_ENTRY |
|
561 if ( aReply->error() != QNetworkReply::NoError ) |
|
562 { |
|
563 TX_LOG_ARGS( "Network request error " << aReply->error() << aReply->errorString() ); |
|
564 // TODO what to do now? |
|
565 } |
|
566 TX_EXIT |
|
567 } |
|
568 |
|
569 #endif // SHARE_FUNC_ENABLED |
|