levelcontroller.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright (c) 2011 Nokia Corporation.
  3. */
  4. #include "levelcontroller.h"
  5. #include "levelmodel.h"
  6. #include "level.h"
  7. #include <QFile>
  8. #include <QTextStream>
  9. #include <QStringList>
  10. #include <QMessageBox>
  11. LevelController::LevelController(Level *level,QObject *parent) :
  12. QObject(parent)
  13. {
  14. /***********************************************************************************************
  15. *Create a level model and a IAPClient object. *
  16. ***********************************************************************************************/
  17. m_Model = new LevelModel(this);
  18. #if defined(IAP_ENABLED)
  19. m_IAPClient = new IAPClient(this);
  20. #endif
  21. /***********************************************************************************************
  22. *Set the level object. *
  23. ***********************************************************************************************/
  24. m_level = level;
  25. /***********************************************************************************************
  26. *Register metatype for IAPClient::ProductData, to be able to use QueuedConnection. *
  27. ***********************************************************************************************/
  28. #if defined(IAP_ENABLED)
  29. qRegisterMetaType<IAPClient::ProductData>("IAPClient::ProductData");
  30. #endif
  31. /***********************************************************************************************
  32. *Make connections. *
  33. ***********************************************************************************************/
  34. #if defined(IAP_ENABLED)
  35. connect(m_IAPClient,SIGNAL(productDataReceived(int,QString,IAPClient::ProductData)),
  36. this,SLOT(productDataReceved(int,QString,IAPClient::ProductData)),Qt::QueuedConnection);
  37. connect(m_IAPClient,SIGNAL(restorableProductsReceived(int,QString,IAPClient::ProductList)),
  38. this,SLOT(restorableProductsReceived(int,QString,IAPClient::ProductList)));
  39. connect(m_IAPClient,SIGNAL(userAndDeviceDataReceived(int,QString,QString,QString,QString,QString,QString)),
  40. this,SLOT(userAndDeviceDataReceived(int,QString,QString,QString,QString,QString,QString)));
  41. connect(m_IAPClient,SIGNAL(purchaseCompleted(int,QString,QString)),
  42. this,SLOT(purchaseCompleted(int,QString,QString)));
  43. connect(m_IAPClient,SIGNAL(purchaseFlowFinished(int)),
  44. this,SLOT(purchaseFlowFinished(int)));
  45. connect(m_IAPClient,SIGNAL(restorationCompleted(int,QString,QString)),
  46. this,SLOT(restorationCompleted(int,QString,QString)));
  47. connect(m_IAPClient,SIGNAL(restorationFlowFinished(int)),
  48. this,SLOT(restorationFlowFinished(int)));
  49. #endif
  50. /**********************************************************************************************/
  51. /***********************************************************************************************
  52. *Load items and set the default theme. *
  53. ***********************************************************************************************/
  54. loadItems();
  55. face = ":/images/face-smile.png";
  56. }
  57. void LevelController::load(int index)
  58. {
  59. /***********************************************************************************************
  60. *Create a ModelIndex for the given index. *
  61. ***********************************************************************************************/
  62. QModelIndex i = m_Model->index(index);
  63. if(i.isValid()){
  64. /*******************************************************************************************
  65. *If index is valid, extract levelId and type. *
  66. *******************************************************************************************/
  67. QString levelId = m_Model->data(i,LevelModel::LevelIdRole).toString();
  68. QString levelType = m_Model->data(i,LevelModel::LevelType).toString();
  69. if(levelId != LEVEL_INVALID){
  70. /***************************************************************************************
  71. *If type == Level, trigger level loading. *
  72. ***************************************************************************************/
  73. if(levelType == "Level" &&
  74. m_level->load(QString("%1.xml").arg(levelId)))
  75. emit levelReload();
  76. /***************************************************************************************
  77. *If type == Theme, extract theme name and set new theme. *
  78. ***************************************************************************************/
  79. else if(levelType == "Theme"){
  80. face = QString(":/images/face-%1.png")
  81. .arg(m_Model->data(i,LevelModel::LevelNameRole).toString());
  82. emit faceChanged();
  83. }
  84. }
  85. }
  86. }
  87. void LevelController::buy(int index)
  88. {
  89. /***********************************************************************************************
  90. *Set initial id to LEVEL_INVALID. *
  91. ***********************************************************************************************/
  92. QString itemId = LEVEL_INVALID;
  93. /***********************************************************************************************
  94. *Create a ModelIndex for the given index. *
  95. ***********************************************************************************************/
  96. QModelIndex i = m_Model->index(index);
  97. if(i.isValid())
  98. /*******************************************************************************************
  99. *If index is valid, extract levelId. *
  100. *******************************************************************************************/
  101. itemId = m_Model->data(i,LevelModel::LevelIdRole).toString();
  102. if(itemId != LEVEL_INVALID){
  103. /*******************************************************************************************
  104. *If id valid, call the IAP purchase method. *
  105. *******************************************************************************************/
  106. #if defined(IAP_ENABLED)
  107. int requestId = m_IAPClient->purchaseProduct(itemId,IAPClient::ForcedAutomaticRestoration);
  108. if(requestId > 0){
  109. /***************************************************************************************
  110. *If requestId valid, extract item name and add a new entry to the request queue. *
  111. ***************************************************************************************/
  112. QString name = m_Model->data(i,LevelModel::LevelNameRole).toString();
  113. levelRequests.insert(requestId,QPair<QString,QString>(itemId,name));
  114. }
  115. #endif
  116. }
  117. }
  118. /***************************************************************************************************
  119. *Simple access methods. *
  120. ***************************************************************************************************/
  121. LevelModel* LevelController::getLevelModel() const
  122. {
  123. return m_Model;
  124. }
  125. QString LevelController::getFace() const
  126. {
  127. return face;
  128. }
  129. /**************************************************************************************************/
  130. #if defined(IAP_ENABLED)
  131. void LevelController::productDataReceved(int requestId,QString status,IAPClient::ProductData data)
  132. {
  133. /***********************************************************************************************
  134. *Check response status. *
  135. ***********************************************************************************************/
  136. if(status.compare("OK",Qt::CaseInsensitive) == 0){
  137. /*******************************************************************************************
  138. *Set initial id,name and type to LEVEL_INVALID and unlocked false. *
  139. *******************************************************************************************/
  140. QString id = LEVEL_INVALID;
  141. QString name = LEVEL_INVALID;
  142. QString type = LEVEL_INVALID;
  143. bool unlocked = false;
  144. /*******************************************************************************************
  145. *Check the request type by looking in the right queue. *
  146. *******************************************************************************************/
  147. if(levelRequests.contains(requestId)){
  148. /***************************************************************************************
  149. *If requested item is a Level, extract id and name, set type to Level. *
  150. *Check if the file "itemId.xml" exists, if positive the level has been unlocked. *
  151. ***************************************************************************************/
  152. QPair<QString,QString> idName = levelRequests.take(requestId);
  153. id = idName.first;
  154. name = idName.second;
  155. type = "Level";
  156. unlocked = QFile::exists(QString("%1.xml").arg(id));
  157. }else if(themeRequests.contains(requestId)){
  158. /***************************************************************************************
  159. *If requested item is a Theme, extract id and name, set type to Theme. *
  160. *Check if the file "itemId" exists, if positive the theme has been unlocked. *
  161. ***************************************************************************************/
  162. QPair<QString,QString> idName = themeRequests.take(requestId);
  163. id = idName.first;
  164. name = idName.second;
  165. type = "Theme";
  166. unlocked = QFile::exists(QString("%1").arg(id));
  167. }
  168. if(type != LEVEL_INVALID){
  169. /***************************************************************************************
  170. *If item type set, create and populate a new LevelData struct. *
  171. ***************************************************************************************/
  172. LevelData levelData;
  173. levelData.id = id;
  174. levelData.data = data;
  175. levelData.name = name;
  176. levelData.type = type;
  177. levelData.unlocked = unlocked;
  178. /***************************************************************************************
  179. *Add item to model. *
  180. ***************************************************************************************/
  181. m_Model->setLevelData(levelData);
  182. }
  183. /*******************************************************************************************
  184. *Call request item data, to request next items data retrieval. *
  185. *******************************************************************************************/
  186. requestItemData();
  187. }
  188. }
  189. void LevelController::restorableProductsReceived(int requestId,QString status,
  190. IAPClient::ProductList data)
  191. {
  192. }
  193. void LevelController::userAndDeviceDataReceived(int requestId,QString account,QString imei,QString imsi,
  194. QString country,QString language,QString deviceModel)
  195. {
  196. }
  197. void LevelController::purchaseCompleted(int requestId,QString status,QString purchaseTicked)
  198. {
  199. /***********************************************************************************************
  200. *Check if the request queue contains the requestId. *
  201. ***********************************************************************************************/
  202. if(levelRequests.contains(requestId)){
  203. /*******************************************************************************************
  204. *Check response status. *
  205. *******************************************************************************************/
  206. if(status.compare("OK",Qt::CaseInsensitive) == 0){
  207. /***************************************************************************************
  208. *If status == OK, unlock purchase item. *
  209. ***************************************************************************************/
  210. unlock(levelRequests.value(requestId).first,purchaseTicked);
  211. }else if(status.compare("RestorableProduct",Qt::CaseInsensitive) == 0){
  212. /***************************************************************************************
  213. *If status == RestorableProduct, start the product restoration flow. *
  214. ***************************************************************************************/
  215. int resotreRequestId = m_IAPClient->restoreProduct(levelRequests.value(requestId).first);
  216. if(resotreRequestId > 0)
  217. levelRequests.insert(resotreRequestId,levelRequests.take(requestId));
  218. }
  219. }
  220. }
  221. void LevelController::purchaseFlowFinished(int requestId)
  222. {
  223. /***********************************************************************************************
  224. *Remove the request form the queue. Emit the hack signal to hide the software keys. *
  225. ***********************************************************************************************/
  226. levelRequests.remove(requestId);
  227. emit hack();
  228. }
  229. void LevelController::restorationCompleted(int requestId,QString status,QString purchaseTicked)
  230. {
  231. /***********************************************************************************************
  232. *Check if the request queue contains the requestId. *
  233. ***********************************************************************************************/
  234. if(levelRequests.contains(requestId)){
  235. /*******************************************************************************************
  236. *Check response status. *
  237. *******************************************************************************************/
  238. if(status.compare("OK",Qt::CaseInsensitive) == 0)
  239. /***************************************************************************************
  240. *If status == OK, unlock purchase item. *
  241. ***************************************************************************************/
  242. unlock(levelRequests.value(requestId).first,purchaseTicked);
  243. }
  244. }
  245. void LevelController::restorationFlowFinished(int requestId)
  246. {
  247. /***********************************************************************************************
  248. *Remove the request form the queue. Emit the hack signal to hide the software keys. *
  249. ***********************************************************************************************/
  250. levelRequests.remove(requestId);
  251. emit hack();
  252. }
  253. #endif
  254. void LevelController::loadItems()
  255. {
  256. /***********************************************************************************************
  257. *Check if the file "Item.data" exists. *
  258. ***********************************************************************************************/
  259. if(QFile::exists("Items.data")){
  260. /*******************************************************************************************
  261. *Open the file in read-only mode. *
  262. *******************************************************************************************/
  263. QFile dataFile("Items.data");
  264. if(dataFile.open(QIODevice::ReadOnly)){
  265. /***************************************************************************************
  266. *Create a QTextStream to read the files contents. *
  267. ***************************************************************************************/
  268. QTextStream readStream(&dataFile);
  269. while(!readStream.atEnd())
  270. {
  271. /***********************************************************************************
  272. *Read line, append read lines to the itemLines list ignoring comment lines. *
  273. ***********************************************************************************/
  274. QString line = readStream.readLine();
  275. if(line.startsWith("#")) continue;
  276. itemLines.append(line);
  277. }
  278. /***************************************************************************************
  279. *Call request item data, to request first items data retrieval. *
  280. ***************************************************************************************/
  281. requestItemData();
  282. }
  283. }
  284. }
  285. void LevelController::requestItemData()
  286. {
  287. /***********************************************************************************************
  288. *Check if there are any pending lines. *
  289. ***********************************************************************************************/
  290. if(itemLines.count() > 0)
  291. {
  292. /*******************************************************************************************
  293. *Split the line using ';'. *
  294. *******************************************************************************************/
  295. QStringList lineContent = itemLines.takeFirst().split(";");
  296. /*******************************************************************************************
  297. *Check if the line contains exactly 3 elements. *
  298. *******************************************************************************************/
  299. if(lineContent.count() == 3){
  300. /***************************************************************************************
  301. *Check item type and add the request to the appropriate queue. *
  302. ***************************************************************************************/
  303. #if defined(IAP_ENABLED)
  304. if(lineContent.at(1).compare("LEVEL",Qt::CaseInsensitive) == 0){
  305. QString name = lineContent.at(2);
  306. int requestId = m_IAPClient->getProductData(lineContent.at(0));
  307. levelRequests.insert(requestId,QPair<QString,QString>(lineContent.at(0),name));
  308. }else if(lineContent.at(1).compare("THEME",Qt::CaseInsensitive) == 0){
  309. QString name = lineContent.at(2);
  310. int requestId = m_IAPClient->getProductData(lineContent.at(0));
  311. themeRequests.insert(requestId,QPair<QString,QString>(lineContent.at(0),name));
  312. }
  313. #endif
  314. }
  315. }
  316. }
  317. void LevelController::unlock(QString itemId,QString purchaseTicket)
  318. {
  319. /***********************************************************************************************
  320. *The unlocking process implemented here is for presentation purposes only. *
  321. *A truly correct unlocking process needs to involve purchaseTicket validation via the *
  322. *Ticket Verification API and downloading the content form a dedicated server. *
  323. ***********************************************************************************************/
  324. Q_UNUSED(purchaseTicket)
  325. /***********************************************************************************************
  326. *Extract LevelData identified by itemId from the model. *
  327. ***********************************************************************************************/
  328. LevelData level = m_Model->getById(itemId);
  329. if(level.id != LEVEL_INVALID){
  330. /*******************************************************************************************
  331. *If level is valid, set unlocked to true. *
  332. *******************************************************************************************/
  333. level.unlocked = true;
  334. /*******************************************************************************************
  335. *Check item type. *
  336. *******************************************************************************************/
  337. if(level.type == "Level"){
  338. /***************************************************************************************
  339. *Create level file. *
  340. ***************************************************************************************/
  341. QFile source(QString(":/levels/%1.xml").arg(level.name));
  342. source.open(QIODevice::ReadOnly);
  343. QFile levelFile(QString("%1.xml").arg(itemId));
  344. levelFile.open(QIODevice::WriteOnly|QIODevice::Truncate);
  345. QTextStream stream(&levelFile);
  346. stream << source.readAll();
  347. }else if(level.type == "Theme"){
  348. /***************************************************************************************
  349. *Create theme file. *
  350. ***************************************************************************************/
  351. QFile levelFile(QString("%1").arg(itemId));
  352. levelFile.open(QIODevice::WriteOnly|QIODevice::Truncate);
  353. QTextStream stream(&levelFile);
  354. stream << level.name;
  355. }
  356. /*******************************************************************************************
  357. *Overwrite model item data *
  358. *******************************************************************************************/
  359. m_Model->setLevelData(level);
  360. }
  361. }