1
/*
2
 * dialer - MeeGo Voice Call Manager
3
 * Copyright (c) 2009, 2010, Intel Corporation.
4
 *
5
 * This program is licensed under the terms and conditions of the
6
 * Apache License, version 2.0.  The full text of the Apache License is at
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 */
10
11
#include "dialerapplication.h"
12
13
#include "common.h"
14
#include "dbustypes.h"
15
#include "dialercontext.h"
16
#include "dbusdialeradapter.h"
17
#include "pacontrol.h"
18
#include "dialer_adaptor.h"
19
20
#include <QDebug>
21
#include <QX11Info>
22
#include <QInputContext>
23
#include <QInputContextFactory>
24
#include <MNotification>
25
#include <iostream>
26
27
#define OFONO_VOICECALLMANAGER_INTERFACE "org.ofono.VoiceCallManager"
28
29
// pulls in X11 headers, so this *must* be last otherwise it might cause odd conflicts with Qt headers
30
#include "atoms.h"
31
32
void messageHandler(QtMsgType type, const char *msg)
33
{
34
    switch (type) {
35
    case QtDebugMsg:
36
        fprintf(stderr, "Debug: %s\n", msg);
37
        break;
38
    case QtWarningMsg:
39
        fprintf(stderr, "Warning: %s\n", msg);
40
        break;
41
    case QtCriticalMsg:
42
        fprintf(stderr, "Critical: %s\n", msg);
43
        break;
44
    case QtFatalMsg:
45
        fprintf(stderr, "Fatal: %s\n", msg);
46
        abort();
47
    }
48
}
49
50
DialerApplication::DialerApplication(int &argc, char **argv) :
51
    QApplication(argc, argv),
52
    orientation(1),
53
    foregroundWindow(0),
54
    mainWindow(NULL)
55
{
56
    TRACE
57
    setApplicationName("dialer");
58
59
    connect(&orientationSensor, SIGNAL(readingChanged()), SLOT(onOrientationChanged()));
60
61
    QString theme = MGConfItem("/meego/ux/theme").value().toString();
62
    QString themeFile = QString("/usr/share/themes/") + theme + "/theme.ini";
63
    if(!QFile::exists(themeFile))
64
    {
65
        // fallback
66
        themeFile = QString("/usr/share/themes/1024-600-10/theme.ini");
67
    }
68
    themeConfig = new QSettings(themeFile, QSettings::NativeFormat, this);
69
70
//    QInputContext *ic = QInputContextFactory::create("MInputContext", 0);
71
//    if(ic)
72
//        setInputContext(ic);
73
74
    qInstallMsgHandler(messageHandler);
75
76
    // Enable dynamic switching between gl and software rendering on systems
77
    // with graphics architectures that see a benifit in memory usage.
78
    MGConfItem *enableSwapItem = new MGConfItem("/meego/ux/EnableDynamicRendering");
79
    m_enableRenderingSwap = enableSwapItem->value().toBool();
80
    delete enableSwapItem;
81
82
    init();
83
84
    mainWindow = QMLMainWindow::instance();
85
}
86
87
void DialerApplication::connectAll()
88
{
89
    TRACE
90
91
    ManagerProxy *m_manager = ManagerProxy::instance();
92
    if (m_manager->modem() &&
93
        m_manager->modem()->isValid() &&
94
        !m_manager->modem()->path().isEmpty())
95
    {
96
        qDebug() << QString("Connecting to CallManager....");
97
        m_manager->setNetwork(m_manager->modem()->path());
98
        m_manager->setCallManager(m_manager->modem()->path());
99
        m_manager->setVolumeManager(m_manager->modem()->path());
100
        m_manager->setVoicemail(m_manager->modem()->path());
101
102
        connect(m_manager->network(), SIGNAL(connected()), this,
103
                                      SLOT(networkConnected()));
104
        connect(m_manager->network(), SIGNAL(disconnected()), this,
105
                                      SLOT(networkDisconnected()));
106
        connect(m_manager->callManager(), SIGNAL(connected()), this,
107
                                          SLOT(callManagerConnected()));
108
        connect(m_manager->callManager(), SIGNAL(disconnected()), this,
109
                                          SLOT(callManagerDisconnected()));
110
        connect(m_manager->callManager(), SIGNAL(callsChanged()), this,
111
                                          SLOT(onCallsChanged()));
112
        connect(m_manager->voicemail(), SIGNAL(messagesWaitingChanged()), this,
113
                                          SLOT(messagesWaitingChanged()));
114
        if (MODE_HFP_ENABLED)
115
        {
116
            PAControl* paControl = PAControl::instance();
117
            connect(m_manager->callManager(), SIGNAL(callsChanged()), paControl,
118
                                              SLOT(onCallsChanged()));
119
        }
120
    }
121
}
122
123
bool DialerApplication::isConnected()
124
{
125
    TRACE
126
    return m_connected;
127
}
128
129
bool DialerApplication::hasError() const
130
{
131
    TRACE
132
    return !m_lastError.isEmpty();
133
}
134
135
QString DialerApplication::lastError() const
136
{
137
    TRACE
138
    return m_lastError;
139
}
140
141
void DialerApplication::setError(const QString msg)
142
{
143
    TRACE
144
    m_lastError.clear();
145
    m_lastError = QString(msg);
146
}
147
148
DialerApplication *DialerApplication::instance()
149
{
150
    TRACE
151
    return qobject_cast<DialerApplication *>(QApplication::instance());
152
}
153
154
SeasideSyncModel *DialerApplication::seasideModel()
155
{
156
    TRACE
157
    return m_seasideModel;
158
}
159
160
SeasideProxyModel *DialerApplication::seasideProxy()
161
{
162
    TRACE
163
    return m_seasideProxy;
164
}
165
166
HistoryTableModel *DialerApplication::historyModel()
167
{
168
    TRACE
169
    return m_historyModel;
170
}
171
172
QSortFilterProxyModel *DialerApplication::historyProxy()
173
{
174
    TRACE
175
    return m_historyProxy;
176
}
177
178
void DialerApplication::init()
179
{
180
    TRACE
181
    m_connected = false;
182
    m_lastError = QString();
183
184
    // Notify Qt of our custom DBus MetaTypes
185
    registerMyDataTypes();
186
187
    m_manager = ManagerProxy::instance();
188
    if (!m_manager || !m_manager->isValid())
189
        //% "Failed to connect to org.ofono.Manager: is ofonod running?"
190
        setError(qtTrId("xx_no_ofono_error"));
191
    else
192
        m_connected = true;
193
194
    DBusDialerAdapter *adapter = new DBusDialerAdapter(this);
195
    if(!adapter)
196
    {
197
        qWarning() << "DBus adapter instantiation failed.";
198
    }
199
200
    if(!QDBusConnection::sessionBus().registerObject(DBUS_SERVICE_PATH, this))
201
    {
202
        qCritical() << "Error registering dbus object:" <<
203
                       QDBusConnection::sessionBus().lastError().message();
204
    }
205
206
    m_seasideModel = new SeasideSyncModel();
207
    m_seasideProxy = new SeasideProxyModel();
208
    m_seasideProxy->setSourceModel(m_seasideModel);
209
    m_seasideProxy->setDynamicSortFilter(true);
210
    m_seasideProxy->setFilterKeyColumn(-1);
211
    m_seasideProxy->setFilterRegExp(MATCH_ALL);
212
    m_seasideProxy->sort(Seaside::ColumnLastName, Qt::AscendingOrder);
213
214
    m_historyModel = new HistoryTableModel();
215
    m_historyProxy = new QSortFilterProxyModel();
216
    m_historyProxy->setSourceModel(m_historyModel);
217
    m_historyProxy->setDynamicSortFilter(true);
218
    m_historyProxy->setFilterKeyColumn(HistoryTableModel::COLUMN_LINEID);
219
    m_historyProxy->sort(HistoryTableModel::COLUMN_CALLSTART,
220
                         Qt::DescendingOrder);
221
222
    connect(m_manager->modem(), SIGNAL(connected()),
223
                                SLOT(modemConnected()));
224
    connect(m_manager->modem(), SIGNAL(disconnected()),
225
                                SLOT(modemDisconnected()));
226
}
227
228
void DialerApplication::modemConnected()
229
{
230
    TRACE
231
    //TODO: Handle multiple modems
232
    if (m_manager->modem() && m_manager->modem()->isValid())
233
    {
234
        m_modem = m_manager->modem();
235
236
        qDebug() << QString("Modem connected");
237
        connect(m_modem, SIGNAL(interfacesChanged(QStringList)), this,
238
                           SLOT(modemInterfacesChanged(QStringList)));
239
        connect(m_modem, SIGNAL(poweredChanged(bool)), this,
240
                           SLOT(modemPowered(bool)));
241
242
        if (m_modem->powered() &&
243
            m_modem->interfaces().contains(OFONO_VOICECALLMANAGER_INTERFACE))
244
        {
245
            /* connect all now, modem is enabled */
246
            qDebug() << QString("Modem is powered: ");
247
            this->connectAll();
248
        }
249
    }
250
}
251
252
void DialerApplication::modemDisconnected()
253
{
254
    TRACE
255
    //TODO: Handle multiple modems
256
}
257
258
void DialerApplication::modemInterfacesChanged(QStringList interfaces)
259
{
260
    TRACE
261
    qDebug() << QString("Modem Interfaces: ") << interfaces;
262
263
    if (interfaces.contains(OFONO_VOICECALLMANAGER_INTERFACE) &&
264
        m_manager->modem()->powered())
265
    {
266
        qDebug() << QString("Modem VoiceCallManager available, connect CallManager");
267
        this->connectAll();
268
    }
269
}
270
271
void DialerApplication::modemPowered(bool isPowered)
272
{
273
    TRACE
274
    qDebug() << QString("Modem Powered: ") << isPowered;
275
276
    if (isPowered &&
277
        m_manager->modem()->interfaces().contains(OFONO_VOICECALLMANAGER_INTERFACE))
278
    {
279
        qDebug() << QString("Modem powered up, connect CallManager");
280
        this->connectAll();
281
    }
282
}
283
284
void DialerApplication::networkConnected()
285
{
286
    TRACE
287
    if (m_manager->network() && m_manager->network()->isValid())
288
        m_network = m_manager->network();
289
}
290
291
void DialerApplication::networkDisconnected()
292
{
293
    TRACE
294
}
295
296
void DialerApplication::callManagerConnected()
297
{
298
    TRACE
299
    if (m_manager->callManager() && m_manager->callManager()->isValid())
300
        m_callManager = m_manager->callManager();
301
302
303
    qDebug() << QString("Disconnect calls changed signal");
304
    disconnect(m_callManager, SIGNAL(callsChanged()));
305
306
    qDebug() << QString("Disconnect incoming signal");
307
    disconnect(m_callManager, SIGNAL(incomingCall(CallItem*)));
308
309
    qDebug() << QString("Disconnect resource lost");
310
    disconnect(m_callManager, SIGNAL(callResourceLost(const QString)));
311
}
312
313
void DialerApplication::callManagerDisconnected()
314
{
315
    TRACE
316
    qDebug() << QString("CallMgr disconnected");
317
}
318
319
void DialerApplication::messagesWaitingChanged()
320
{
321
    TRACE
322
    static MNotification *vmail = NULL;
323
324
    if (!m_manager->voicemail() || !m_manager->voicemail()->isValid()) {
325
        qDebug() << QString("Voicemail proxy is invalid, ignoring");
326
        return;
327
    }
328
329
330
    if (!vmail) {
331
        bool found = false;
332
        foreach (MNotification *notice, MNotification::notifications()) {
333
            if (notice->eventType() == MNotification::MessageArrivedEvent) {
334
                // If we've already found a MessageArrived notification,
335
                // we must delete others since we only want one
336
                if (found) {
337
                    qDebug() << QString("Removing duplicate voicemail notice");
338
                    notice->remove();
339
                    delete notice;
340
                }
341
                else {
342
                    vmail = notice;
343
                    found = true;
344
                }
345
            }
346
            else {
347
                // We're only interested in MessageArrived events, all others
348
                // can need to be deleted here since they are copies
349
                delete notice;
350
            }
351
        }
352
353
        if (!found) {
354
            if (!m_manager->voicemail()->waiting()) {
355
                qDebug() << QString("No waiting Voicemail messages");
356
                return;
357
            }
358
            else {
359
                // This is the first instance of a MessageArrived event
360
                qDebug() << QString("Creating new voicemail notice instance");
361
                vmail = new MNotification(MNotification::MessageArrivedEvent);
362
                vmail->setCount(0);
363
            }
364
        }
365
        else {
366
            if (!m_manager->voicemail()->waiting()) {
367
                qDebug() << QString("No waiting Voicemail messages");
368
                vmail->remove();
369
                return;
370
            }
371
        }
372
    }
373
374
    // We've got a valid notification and we have messages waiting...
375
    int vCount = m_manager->voicemail()->count();
376
    int nCount = vmail->count();
377
378
    if (vCount < 0) vCount = 0;
379
    if (nCount < 0) nCount = 0;
380
381
    qDebug() << QString("Voicemails: %1").arg(QString::number(vCount));
382
    qDebug() << QString("Notices:    %1").arg(QString::number(nCount));
383
384
    // No more Voicemail messages waiting, remove notification
385
    if (vCount <= 0) {
386
        qDebug() << QString("No Voicemails waiting, removing notification");
387
        vmail->remove();
388
        return;
389
    }
390
391
    // The waiting voicemail count has changed, update and [re]publish it
392
    //% "You have %1 voice messages"
393
    vmail->setSummary(qtTrId("xx_messages_waiting").arg(vCount));
394
    vmail->setImage("icon-m-telephony-voicemail");
395
    vmail->setCount(vCount);
396
    qDebug() << QString("Voicemail count changed, publishing notification");
397
    vmail->publish();
398
}
399
400
void DialerApplication::onCallsChanged()
401
{
402
    TRACE
403
}
404
405
bool DialerApplication::x11EventFilter(XEvent *event)
406
{
407
    Display *dpy = QX11Info::display();
408
409
    Atom activeWindowAtom = getAtom(ATOM_NET_ACTIVE_WINDOW);
410
411
    // Foreground window detection
412
    if (event->type == PropertyNotify &&
413
            event->xproperty.atom == activeWindowAtom)
414
    {
415
        Atom actualType;
416
        int actualFormat;
417
        unsigned long numWindowItems, bytesLeft;
418
        unsigned char *data = NULL;
419
420
        int result = XGetWindowProperty(dpy,
421
                                        DefaultRootWindow(dpy),
422
                                        activeWindowAtom,
423
                                        0, 0x7fffffff,
424
                                        false, XA_WINDOW,
425
                                        &actualType,
426
                                        &actualFormat,
427
                                        &numWindowItems,
428
                                        &bytesLeft,
429
                                        &data);
430
431
        if (result == Success && data != None)
432
        {
433
            Window w = *(Window *)data;
434
            XFree(data);
435
436
            if (foregroundWindow != (int)w && !isSystemModelDialog(w))
437
            {
438
                foregroundWindow = (int)w;
439
                emit foregroundWindowChanged();
440
441
                if (m_enableRenderingSwap)
442
                {
443
                    if (mainWindow)
444
                    {
445
                        if (mainWindow->winId() == (int)w)
446
                        {
447
                            mainWindow->switchToGLRendering();
448
                        }
449
                        else
450
                        {
451
                            mainWindow->switchToSoftwareRendering();
452
                        }
453
                    }
454
                }
455
            }
456
        }
457
    }
458
459
    return QApplication::x11EventFilter(event);
460
}
461
462
void DialerApplication::setOrientationLocked(bool locked)
463
{
464
    orientationLocked = locked;
465
    setOrientationSensorOn(!locked);
466
}
467
468
// Copied from libmeegotouch, which we don't link against.  We need it
469
// defined so we can connect a signal to the MInputContext object
470
// (loaded from a plugin) that uses this type.
471
namespace M {
472
    enum OrientationAngle { Angle0=0, Angle90=90, Angle180=180, Angle270=270 };
473
}
474
475
void DialerApplication::onOrientationChanged()
476
{
477
    TRACE
478
    int orientation = orientationSensor.reading()->orientation();
479
480
    int qmlOrient = -1;
481
    M::OrientationAngle mtfOrient;
482
    switch (orientation)
483
    {
484
    case QOrientationReading::LeftUp:
485
        mtfOrient = M::Angle270;
486
        qmlOrient = 2;
487
        break;
488
    case QOrientationReading::TopDown:
489
        mtfOrient = M::Angle180;
490
        qmlOrient = 3;
491
        break;
492
    case QOrientationReading::RightUp:
493
        mtfOrient = M::Angle90;
494
        qmlOrient = 0;
495
        break;
496
    case QOrientationReading::TopUp:
497
        mtfOrient = M::Angle0;
498
        qmlOrient = 1;
499
        break;
500
    default:
501
        // ignore faceup and facedown events
502
        break;
503
    }
504
505
    if (qmlOrient == -1)
506
        return;
507
508
    setOrientation(qmlOrient);
509
510
    // Need to tell the MInputContext plugin to rotate the VKB too
511
//    QMetaObject::invokeMethod(inputContext(),
512
//                              "notifyOrientationChanged",
513
//                              Q_ARG(M::OrientationAngle, mtfOrient));
514
}
515
516
void DialerApplication::setOrientationSensorOn(bool value)
517
{
518
    if (value && !orientationSensor.isActive())
519
    {
520
        orientationSensor.start();
521
    }
522
    else if(!value && orientationSensor.isActive())
523
    {
524
        orientationSensor.stop();
525
    }
526
}
527
528
bool DialerApplication::isSystemModelDialog(unsigned target)
529
{
530
    Atom actualType;
531
    int actualFormat;
532
    unsigned long numWindowItems, bytesLeft;
533
    unsigned char *data = NULL;
534
535
    int result = XGetWindowProperty(QX11Info::display(),
536
                                    target,
537
                                    getAtom(ATOM_MEEGO_SYSTEM_DIALOG),
538
                                    0, 0x7fffffff,
539
                                    false, XA_WINDOW,
540
                                    &actualType,
541
                                    &actualFormat,
542
                                    &numWindowItems,
543
                                    &bytesLeft,
544
                                    &data);
545
546
    if (result == Success && data != None)
547
    {
548
        XFree(data);
549
        return true;
550
    }
551
552
    return false;
553
}