/*
* This file is part of Jenirok.
*
* Jenirok is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Jenirok is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jenirok. If not, see .
*
*/
#include
#include
#include
#include "calllistener.h"
#include "settings.h"
#include "cache.h"
#include "contactmanager.h"
#include "connectionmanager.h"
#include "sourcecoreconfig.h"
#include "db.h"
namespace
{
const QString CALL_SERVICE_NAME = "com.nokia.csd";
const QString CALL_SERVICE_PATH = "/com/nokia/csd/call";
const QString CALL_SERVICE_INTERFACE = "com.nokia.csd.Call";
const QString CALL_SERVICE_INSTANCE_NAME = "com.nokia.csd.Call.Instance";
const QString CALL_SIGNAL_INCOMING = "Coming";
const QString CALL_SIGNAL_RELEASE = "Release";
const QString CALL_SIGNAL_TERMINATED = "Terminated";
const QString CALL_SIGNAL_ANSWERED = "AudioConnect";
}
QDBusConnection CallListener::systemBus_ = QDBusConnection::systemBus();
CallListener::CallListener(): source_(0),
closeConnection_(false), initialized_(false), box_(0), label_(0),
retries_(-1), timer_(0), currentCall_(0)
{
}
CallListener::~CallListener()
{
end();
DB::removeDatabase();
}
bool CallListener::begin()
{
if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
{
qDebug() << "Bad connection settings, unable to start";
return false;
}
sourceId_ = Source::stringToId(Settings::instance()->get("source"));
QMap tmpConfig;
SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
config->getConfig(tmpConfig);
sourceConfig_ = tmpConfig;
delete config;
systemBus_.connect(CALL_SERVICE_NAME,
CALL_SERVICE_PATH,
CALL_SERVICE_INTERFACE,
CALL_SIGNAL_INCOMING,
this,
SLOT(incomingCall(QDBusObjectPath, QString)));
systemBus_.connect(CALL_SERVICE_NAME,
CALL_SERVICE_PATH,
CALL_SERVICE_INTERFACE,
CALL_SIGNAL_RELEASE,
this,
SLOT(callTerminate()));
qDebug() << "Starting...";
return true;
}
void CallListener::end()
{
systemBus_.disconnect(CALL_SERVICE_NAME,
CALL_SERVICE_PATH,
CALL_SERVICE_INTERFACE,
CALL_SIGNAL_INCOMING,
this,
SLOT(incomingCall(QDBusObjectPath, QString)));
systemBus_.disconnect(CALL_SERVICE_NAME,
CALL_SERVICE_PATH,
CALL_SERVICE_INTERFACE,
CALL_SIGNAL_RELEASE,
this,
SLOT(callTerminate()));
searchClose();
sourceConfig_.clear();
}
void CallListener::search(Source::SearchDetails const& details)
{
if(currentCall_)
{
delete currentCall_;
}
currentCall_ = new CallDetails;
currentCall_->number = details.query;
currentCall_->time = QDateTime::currentDateTime().toTime_t();
currentCall_->answered = false;
searchInit();
Source::Result result;
if(Cache::instance().findItem(details.query, result))
{
qDebug() << "Found from cache";
showDelayedResult(createResult(result.name,
result.street,
result.city), BANNER_DELAY);
currentCall_->result = result;
}
else
{
retries_ = 0;
if(!handleConnection())
{
qDebug() << "Unable to connect";
return;
}
showDelayedResult(tr("Searching..."), BANNER_DELAY);
source_->search(details);
}
}
void CallListener::requestFinished(QVector const& results,
Source::SearchDetails const& details,
bool error)
{
/*if(closeConnection_)
{
closeConnection_ = false;
ConnectionManager cm;
cm.disconnect(true);
}*/
qDebug() << "Request finished";
// If box is not visible, the call must have been terminated already
if(!initialized_ || !box_->isVisible() || !currentCall_)
{
return;
}
QString message;
if(error)
{
qDebug() << "Error: " << source_->errorString();
if(retries_ < SEARCH_RETRIES && retries_ >= 0)
{
retries_++;
source_->search(Source::SearchDetails(currentCall_->number, "", Source::BOTH));
return;
}
else
{
timedMessage_ = "";
QString errorString;
Source::Error error = source_->error();
switch(error)
{
case Source::TIMEOUT:
errorString = tr("Request timed out");
break;
default:
errorString = source_->errorString();
break;
}
showError(tr("Searching failed:") + " " + errorString + ".");
}
}
else
{
timedMessage_ = "";
if(results.size() == 0)
{
message = tr("Phone number was not found");
showResult(message);
}
else
{
message = createResult(results.at(0).name, results.at(0).street,
results.at(0).city);
showResult(message);
Source::Result result = results.at(0);
result.number = details.query;
Cache::instance().addItem(result);
currentCall_->result = results.at(0);
}
}
retries_ = -1;
}
QString CallListener::createResult(QString const& name, QString const& street, QString const& city)
{
QString result = "" + name + "";
if(!street.isEmpty() || !city.isEmpty())
{
result += "
";
if(!street.isEmpty())
{
result += street + ", ";
}
result += city;
}
return result;
}
void CallListener::showResult(QString const& text)
{
if(!initialized_)
{
return;
}
label_->setText("" + text + "");
if(box_->isVisible())
{
box_->hide();
}
box_->show();
}
void CallListener::incomingCall(QDBusObjectPath path, QString number)
{
if(number.isEmpty())
{
qDebug() << "Unknown caller without number";
return;
}
ContactManager cm;
if(!cm.numberExists(number))
{
qDebug() << "Number doesn't exist: " << number;
systemBus_.connect(CALL_SERVICE_NAME,
path.path(),
CALL_SERVICE_INSTANCE_NAME,
CALL_SIGNAL_TERMINATED,
this,
SLOT(callTerminate()));
systemBus_.connect(CALL_SERVICE_NAME,
path.path(),
CALL_SERVICE_INSTANCE_NAME,
CALL_SIGNAL_ANSWERED,
this,
SLOT(handleAnswer()));
search(Source::SearchDetails(number, "", Source::BOTH));
}
else
{
qDebug() << "Number exists: " << number;
}
}
void CallListener::callTerminate()
{
if(box_ && box_->isVisible())
{
box_->hide();
}
if(currentCall_)
{
currentCall_->result.number = currentCall_->number;
Cache::instance().logItem(currentCall_->result, !currentCall_->answered, currentCall_->time);
delete currentCall_;
currentCall_ = 0;
}
searchClose();
}
void CallListener::handleAnswer()
{
qDebug() << "Answered";
if(currentCall_)
{
currentCall_->answered = true;
}
}
void CallListener::showDelayedResult(QString const& text, int delay)
{
timedMessage_ = text;
QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
}
void CallListener::showTimedMessage()
{
if(timedMessage_.size() == 0 || !initialized_)
{
return;
}
showResult(timedMessage_);
timedMessage_ = "";
}
void CallListener::searchInit()
{
qDebug() << "Initializing search...";
if(initialized_)
{
qDebug() << "Already initialized";
return;
}
source_ = Source::getSource(sourceId_);
SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
config->loadFromConfig(sourceConfig_);
config->apply(source_);
delete config;
source_->setMaxResults(1);
source_->setFindNumber(false);
source_->setTimeout(REQUEST_TIMEOUT);
connect(source_, SIGNAL(requestFinished(QVector const&,
Source::SearchDetails const&, bool)),
this, SLOT(requestFinished(QVector const&,
Source::SearchDetails const&, bool)));
box_ = new InformationBox;
label_ = new QLabel("", box_);
label_->setMargin(8);
label_->setWordWrap(true);
box_->setWidget(label_);
initialized_ = true;
}
void CallListener::searchClose()
{
if(!initialized_)
{
return;
}
initialized_ = false;
qDebug() << "Closing search...";
if(source_)
{
disconnect(source_, SIGNAL(requestFinished(QVector const&,
Source::SearchDetails const&, bool)),
this, SLOT(requestFinished(QVector const&,
Source::SearchDetails const&, bool)));
}
delete source_;
source_ = 0;
delete box_;
box_ = 0;
label_ = 0;
if(closeConnection_)
{
QTimer::singleShot(500, this, SLOT(closeConnection()));
}
}
void CallListener::closeConnection()
{
if(closeConnection_)
{
closeConnection_ = false;
ConnectionManager cm;
cm.disconnect(true);
}
}
bool CallListener::handleConnection()
{
if(!initialized_)
{
return false;
}
ConnectionManager cm;
if(cm.isConnected())
{
cm.connect();
closeConnection_ = false;
return true;
}
closeConnection_ = true;
Settings::ConnectionType configType = Settings::instance()->getConnectionType();
if(configType == Settings::ALWAYS_ASK)
{
showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
return false;
}
showDelayedResult(tr("Connecting..."), BANNER_DELAY);
ConnectionManager::Connection best;
ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
switch(configType)
{
case Settings::WLAN:
lookupType = ConnectionManager::WLAN;
break;
case Settings::GPRS:
lookupType = ConnectionManager::GPRS;
break;
default:
lookupType = ConnectionManager::NO_TYPE;
break;
}
int cretries = 0;
int scans = 0;
bool found = false;
int maxScans = GPRS_SCANS;
if(lookupType != ConnectionManager::GPRS)
{
maxScans = WLAN_SCANS;
}
while(cretries < CONNECTION_LOOKUP_RETRIES)
{
if(!initialized_)
{
return false;
}
if(scans < maxScans)
{
if(cm.getBestConnection(best, lookupType))
{
found = true;
}
scans++;
}
// If there is only gprs connection available,
// make sure that we are on 3g network
if(found && (best.type != ConnectionManager::GPRS || is3g()))
{
break;
}
if(found)
{
sleep(WAIT_BETWEEN_RETRIES);
}
qDebug() << "No connections found, retrying...";
cretries++;
}
if(cretries >= CONNECTION_LOOKUP_RETRIES)
{
showError(tr("No available 3G or WLAN networks found."));
return false;
}
int retries = 0;
while(retries < CONNECT_RETRIES)
{
if(!initialized_)
{
return false;
}
qDebug() << "Connecting to " << best.name << " (" << best.id << ")";
if(cm.connect(best.id))
{
break;
}
else if(cm.error() == ConnectionManager::INVALID_IAP)
{
showError(tr("Selected access point doesn't exist."));
return false;
}
retries++;
qDebug() << "Unable to connect, retrying...";
if(retries < CONNECT_RETRIES)
{
sendRetrySignal(best.id, false);
sleep(WAIT_BETWEEN_RETRIES);
}
}
if(retries >= CONNECT_RETRIES)
{
sendRetrySignal(best.id, false);
if(initialized_)
{
showError(tr("Unable to connect to network."));
}
return false;
}
return initialized_;
}
void CallListener::showError(QString const& msg, int timeout)
{
qDebug() << "Error: " << msg;
if(!initialized_ || !box_)
{
return;
}
box_->setTimeout(ERROR_BANNER_TIMEOUT);
if(timeout)
{
showDelayedResult(msg, timeout);
}
else
{
showResult(msg);
}
}
bool CallListener::is3g()
{
QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.phone.net",
"/com/nokia/phone/net",
"Phone.Net",
"get_registration_status");
QDBusMessage rep = systemBus_.call(msg);
if(rep.type() == QDBusMessage::ErrorMessage)
{
qDebug() << "Unable to get network status";
return false;
}
uint status = rep.arguments().value(6).toUInt();
if(status & 0x10 || status & 0x08)
{
return true;
}
return false;
}
void CallListener::sendRetrySignal(QString const& iap, bool retry)
{
QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
"com.nokia.icd_ui",
"retry");
QList arguments;
arguments.append(QVariant(iap));
arguments.append(QVariant(retry));
msg.setArguments(arguments);
QDBusConnection::systemBus().send(msg);
qDebug() << "Retry signal sent";
}
void CallListener::timerEvent(QTimerEvent* event)
{
Q_UNUSED(event);
killTimer(timer_);
timer_ = 0;
}
void CallListener::sleep(int ms)
{
if(timer_)
{
killTimer(timer_);
}
timer_ = startTimer(ms);
while(timer_)
{
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
}