123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- #include "buycatalog.h"
- #include "productinfo.h"
- #include "mainwindow.h"
- #include <QtCore/QFile>
- #include <QtCore/QTextStream>
- #include <QtGui/QApplication>
- #include <QtCore/QDebug>
- #include <QtCore/QMetaType>
- #include <QtGui/QMessageBox>
- #include <QtCore/QDirIterator>
- #include <QProgressDialog>
- Q_DECLARE_METATYPE(IAPClient::ProductDataList) //to be able to transfer data this type with Qt::QueuedConnection
- #define INVALID_VALUE -1
- /*
- * BuyCatalog - a QDialog inherited class wich will display a list of available
- * products allowing the user to trigger the In-Application Purchase experience
- * at a click of a button.
- *
- * This constuctor reads a list of product IDs from the catalog.dat file and
- * asks for the corresponding product data (IAPClient::ProductData) which the
- * API will fetch from Ovi.
- */
- BuyCatalog::BuyCatalog(QWidget *parent)
- : QDialog(parent),
- productsRequested(0),
- current_requestId(INVALID_VALUE),
- infoDialog(NULL)
- {
- // required so that IAPClient::ProductData can be queued in the signal
- qRegisterMetaType<IAPClient::ProductDataHash>("IAPClient::ProductDataHash");
- qDebug() << "::. Catalog constuctor";
- iap_client = new IAPClient(this);
- ui.setupUi(this);
- ui.listWidget->setStyleSheet(QString::fromUtf8("QListWidget{ background-color: grey; border-style: outset; border-width: 1px; border-radius: 1px; border-color: black; selection-color: black; selection-background-color: red; }" \
- "QPushButton{ background-color: green; border-style: outset; border-width: 2px; border-radius: 10px; border-color: beige; font: bold 14px; min-width: 4em; max-width:5em; padding: 6px; }" \
- "QPushButton:pressed { background-color: rgb(224, 0, 0); border-style: inset;}"));
- connect(ui.listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(moreProductInfo(QListWidgetItem*)));
- Q_ASSERT(parent);
- connect(ui.backButton, SIGNAL(clicked()), parent, SLOT(catalogClosed()));
- connect(ui.restoreButton, SIGNAL(clicked()), this, SLOT(restoreProducts()));
- ui.backButton->setEnabled(false);
- ui.restoreButton->setEnabled(false);
- QString filepath = QApplication::applicationDirPath();
- filepath.append("/drm/data");
- QFile file(filepath);
- file.open(QFile::ReadOnly);
- QDirIterator dirit(filepath, QDir::Dirs, QDirIterator::NoIteratorFlags);
- while(dirit.hasNext())
- {
- QString dir = dirit.next();
- if(dir.contains("resourceid_", Qt::CaseInsensitive))
- {
- // breakdown the !:/private/<SID>/drm/data/resourceid_<productID>/
- // extract the product to which this file belongs
- QString product;
- QStringList elem = dir.split("_", QString::SkipEmptyParts);
- elem=elem[1].split("/", QString::SkipEmptyParts);
- product = elem[0].trimmed();
- if(!product.isEmpty())
- {
- if(!isProductActivated(product))
- {
- products.append(product);
- qDebug() << "++ ProductID " << product << " to be puchased.";
- }
- else
- {
- qDebug() << "++ ProductID " << product << " is activated.";
- }
- }
- }
- }
- //qDebug() << "Products list count: " << products.count();
- if(products.count()>0)
- {
- // connect IAP API's signals to app's slots
- connect(iap_client, SIGNAL(productDataReceived( int, QString, IAPClient::ProductDataHash)), this,
- SLOT(productDataReceived(int, QString, IAPClient::ProductDataHash)), Qt::QueuedConnection);
- connect(iap_client, SIGNAL(purchaseCompleted( int , QString, QString)), this,
- SLOT(purchaseCompleted( int , QString, QString)),Qt::QueuedConnection);
- connect(iap_client, SIGNAL(purchaseFlowFinished(int)), this, SLOT(purchaseFlowFinished(int)), Qt::QueuedConnection);
- ui.progressBar->setRange(0, products.count());
- ui.progressBar->setTextVisible(false);
- // request product data from Ovi for the products
- requestNextProduct();
- }
- else
- ui.progressBar->setVisible(false);
- ui.backButton->setEnabled(true);
- connect(iap_client, SIGNAL(restorableProductsReceived( int, QString, IAPClient::ProductDataList)), this,
- SLOT(restorableProductsReceived( int, QString, IAPClient::ProductDataList)));
- connect(iap_client, SIGNAL(restorationCompleted(int, QString, QString)), this,
- SLOT(restorationCompleted(int, QString, QString)),Qt::QueuedConnection);
- busyIndicator = new QProgressDialog("", "", 1, 1, this);
- busyIndicator->setCancelButton(NULL);
- busyIndicator->setWindowModality(Qt::WindowModal);
- qDebug() << ".:: Catalog constuctor";
- }
- /*
- *
- *
- *
- */
- BuyCatalog::~BuyCatalog()
- {
- delete infoDialog;
- delete iap_client;
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::infoDialogClosed(int result)
- {
- if(result==QDialog::Accepted)
- {
- IAPClient::ProductDataHash info = available_products[current_product_index];
- current_requestId = iap_client->purchaseProduct(info.value("id").toString(), IAPClient::ForcedAutomaticRestoration);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::moreProductInfo(QListWidgetItem* item)
- {
- qDebug() << "::. moreProductInfo";
- if(infoDialog!=NULL)
- {
- disconnect(infoDialog, SIGNAL(finished(int)), this, SLOT(infoDialogClosed(int)));
- delete infoDialog;
- infoDialog = NULL;
- }
- current_product_index = ui.listWidget->row(item);
- IAPClient::ProductDataHash info = available_products[current_product_index];
- // create a dialog for showing detailed product info
- infoDialog = new ProductInfo(info.value("info").toString(), info.value("description").toString(), info.value("price").toString(), this);
- // get notified when the product info dialog is closed
- connect(infoDialog, SIGNAL(finished(int)), this, SLOT(infoDialogClosed(int)));
- // show the dialog
- infoDialog->showFullScreen();
- qDebug() << ".:: moreProductInfo";
- }
- /*
- * Returns true if the product files can be accessed or false if the purchase/restore is needed
- */
- bool BuyCatalog::isProductActivated(QString product)
- {
- // ToDo: construct path, find first file and attempt to open it
- // return false if open request fails with DRM access code
- return false;
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::buyProduct()
- {
- qDebug() << "::. buyProduct";
- // locate the list item which holds the button that send a signal to this slot
- QWidget *clickedWidget = qobject_cast<QWidget *>(sender()->parent());
- for (int index = 0; index < ui.listWidget->count(); index++)
- {
- QListWidgetItem *runningItem = ui.listWidget->item(index);
- QWidget *widget = ui.listWidget->itemWidget(runningItem);
- if (clickedWidget == widget)
- {
- IAPClient::ProductDataHash info = available_products[index];
- // issue a buy request for the corresponding product
- // if the user has paid for this content already, restoration of the license is needed
- current_productId = info.value("id").toString();
- current_requestId = iap_client->purchaseProduct(current_productId, IAPClient::ForcedAutomaticRestoration);
- break;
- }
- }
- qDebug() << ".:: buyProduct";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::productDataReceived( int requestId, QString status, IAPClient::ProductDataHash productData )
- {
- qDebug() << "::. productDataReceived";
- //Q_ASSERT(requestId == current_requestId);
-
- if(QString::compare(status, "OK", Qt::CaseInsensitive)==0)
- {
- available_products.append(productData);
- // add a new item to the products catalog UI's list
- QListWidgetItem *item = new QListWidgetItem();
- ui.listWidget->addItem(item);
- QLabel *labelTitle = new QLabel(productData.value("info").toString());
- QLabel *labelPrice = new QLabel(productData.value("price").toString());
- QHBoxLayout *layoutTitleAndPrice= new QHBoxLayout();
- layoutTitleAndPrice->addWidget(labelTitle);
- layoutTitleAndPrice->addWidget(labelPrice);
- QPushButton *button = new QPushButton(tr("Buy..."));
- QLabel *labelShort = new QLabel(productData.value("shortdescription").toString());
- QHBoxLayout *layoutShortBuy= new QHBoxLayout();
- layoutShortBuy->addWidget(labelShort);
- layoutShortBuy->addWidget(button);
- QVBoxLayout *layoutItem = new QVBoxLayout();
- layoutItem->addItem(layoutTitleAndPrice);
- layoutItem->addItem(layoutShortBuy);
- QWidget *widget = new QWidget();
- widget->setLayout(layoutItem);
- item->setSizeHint(widget->sizeHint());
- ui.listWidget->setItemWidget(item, widget);
- // if multiple products are available, let's highlight the one that triggered the shopping session
- if( productData.value("id").toString() == defaultProduct)
- ui.listWidget->setCurrentItem(item);
- connect(button, SIGNAL(clicked()), this, SLOT(buyProduct()));
- }
- else // what are all the possible status messages?
- {
- // the product is not available for one reason or another?
- // probably you can ignore this
- // or report it to your server for analysis
- qDebug() << "Requested product could not be retrived: " << status;
- }
- // if we don't have any more products ...
- if(productsRequested == products.count())
- {
- ui.progressBar->setVisible(false);
- // have we got any product data?
- if(!available_products.count())
- {
- QMessageBox message;
- message.setText(tr("No products available. Please try again later!"));
- message.exec();
- // close catalog
- reject();
- }
- }
- else
- {
- // let's look for the next product's data
- requestNextProduct();
- }
- qDebug() << ".:: productDataReceived";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::requestNextProduct()
- {
- qDebug() << "::. requestNextProduct";
- if(productsRequested < products.count())
- {
- QString prod = products[productsRequested++];
- current_requestId = iap_client->getProductData(prod);
- qDebug() << "+ Request for product" << prod << " returned id: " << current_requestId;
- ui.progressBar->setValue(productsRequested-1);
- }
- else
- ui.progressBar->setValue(productsRequested);
- if(productsRequested == products.count() - 1){
- ui.restoreButton->setEnabled(true);
- }
- qDebug() << ".:: requestNextProduct";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::purchaseCompleted( int requestId, QString status, QString purchaseTicket )
- {
- qDebug() << "::. purchaseCompleted with status: " << status;
- if(requestId != current_requestId)
- return;
- if(QString::compare(status, "OK", Qt::CaseInsensitive)==0
- || QString::compare(status, "RestorableProduct", Qt::CaseInsensitive)==0)
- {
- MainWindow::saveTicket(purchaseTicket, current_productId);
- // In-Application Purchase API will take care of downloading the DRM license
- // the files covered by this license will become accessible
- }
- else
- {
- // some error
- // error message already displayed by In-Application Purchase UI, may also be reflected in app's UI.
- }
- qDebug() << ".:: purchaseCompleted";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::userAndDeviceDataReceived( int /*requestId*/, QString /*status*/,
- IAPClient::UserAndDeviceDataHash /*userdata*/ )
- {
- qDebug() << "::. userAndDeviceDataReceived";
- qDebug() << ".:: userAndDeviceDataReceived";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorableProductsReceived( int requestId, QString status,
- IAPClient::ProductDataList items )
- {
- qDebug() << "::. restorableProductsReceived with status: " << status;
- if(requestId != current_requestId){
- qDebug() << "::. restorableProductsReceived failed ";
- return;
- }
- current_requestId = INVALID_VALUE;
- if(!items.empty()){
- restorableProductItems.append(items);
- current_productId = restorableProductItems.takeFirst().value("id").toString();
- current_requestId = iap_client->restoreProduct(current_productId);
- }
- if(current_requestId == INVALID_VALUE){
- busyIndicator->close();
- ui.backButton->click();
- }else{
- ui.progressBar->setRange(0, restorableProductItems.count() + 1);
- ui.progressBar->setTextVisible(false);
- ui.progressBar->setVisible(true);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorationFlowFinished( int requestId )
- {
- qDebug() << "::. restorationFlowFinished";
- qDebug() << ".:: restorationFlowFinished";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorationCompleted( int requestId, QString status, QString purchaseTicket )
- {
- qDebug() << "::. restorationCompleted with status: " << status;
- purchaseCompleted(requestId, status, purchaseTicket);
- if(restorableProductItems.empty()){
- busyIndicator->close();
- ui.backButton->click();
- }else{
- ui.progressBar->setValue(ui.progressBar->value() + 1);
- current_productId = restorableProductItems.takeFirst().value("id").toString();
- current_requestId = iap_client->restoreProduct(current_productId);
- }
- qDebug() << ".:: restorationCompleted";
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::purchaseFlowFinished( int requestId )
- {
- qDebug() << "::. purchaseFlowFinished";
- // now the user is back in app's UI
- qDebug() << ".:: purchaseFlowFinished";
- }
- void BuyCatalog::restoreProducts()
- {
- current_requestId = iap_client->getRestorableProducts();
- if(current_requestId < 0)
- return;
- //lock ui
- busyIndicator->setLabelText("restoration in progress...");
- busyIndicator->show();
- restorableProductItems.clear();
- }
- // End of the file.
|