BuyGuidesView.java

/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */
package com.nokia.example.attractions.views;

import com.nokia.example.attractions.Visual;
import com.nokia.example.attractions.views.list.List;
import com.nokia.example.attractions.views.list.NewGuideDrawer;
import com.nokia.example.attractions.models.Guide;
import com.nokia.example.attractions.network.NewGuidesOperation;
import com.nokia.example.attractions.utils.UIUtils;
import com.nokia.mid.payment.IAPClientPaymentException;
import com.nokia.mid.payment.IAPClientPaymentListener;
import com.nokia.mid.payment.IAPClientPaymentManager;
import com.nokia.mid.payment.IAPClientProductData;
import com.nokia.mid.payment.IAPClientUserAndDeviceData;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Graphics;

/**
 * View to display a list of purchasable guides
 */
public final class BuyGuidesView
    extends BaseView
    implements IAPClientPaymentListener {

    private static final String NEW_GUIDES_URL =
        "http://fn-iap-repo.cloudapp.net/api/touristattractions";
    private static final String GUIDE_URL_PREFIX = NEW_GUIDES_URL + "/";
    private final Command buyCmd = UIUtils.createCommand(UIUtils.BUY);
    private volatile String account = null;
    private volatile boolean loadingAccount = false;
    private volatile Vector guides = null;
    private volatile boolean loadingGuides = false;
    private final List guidesList;
    private final IAPClientPaymentManager manager;
    private volatile Guide purchasing = null;
    private Hashtable waitingProductData;
    private final Hashtable cache = new Hashtable();

    BuyGuidesView()
        throws IAPClientPaymentException {
        guidesList = List.getList(new NewGuideDrawer(viewMaster, viewMaster.
            getDefaultThumbnailIcon(),
            cache),
            new List.Listener() {

                public void select(int focusedRowIndex) {
                    selectGuide(focusedRowIndex);
                }
            });
        manager = IAPClientPaymentManager.getIAPClientPaymentManager();
        IAPClientPaymentManager.setIAPClientPaymentListener(this);
    }

    public final void resize(int x, int y, int width, int height) {
        super.resize(x, y, width, height);
        guidesList.resize(x, y, width, height);
    }

    public final void activate() {
        viewMaster.setTitle("Buy guide");
        if (manager == null) {
            guides = new Vector();
        }
        else if (account == null) {
            loadAccountAndGuides();
        }
        else if (guides == null) {
            loadGuides();
        }
        showGuides();
        super.activate();
        viewMaster.addCommand(backCmd);
        if (!viewMaster.hasPointerEvents()) {
            viewMaster.addCommand(buyCmd);
        }
    }

    private void showGuides() {
        if (guides == null || guides.isEmpty()) {
            return;
        }
        guidesList.setData(guides);
        guidesList.enable();
    }

    public final void deactivate() {
        viewMaster.removeCommand(buyCmd);
        super.deactivate();
        guidesList.disable();
        cache.clear();
    }

    public final void draw(final Graphics g) {
        if (!isActive()) {
            return;
        }
        if (guides == null || purchasing != null) {
            drawLoading(g);
        }
        else if (guides.isEmpty()) {
            drawNoGuides(g);
        }
        else {
            guidesList.draw(g);
        }
    }

    private void drawNoGuides(final Graphics g) {
        ViewMaster.drawBackground(g, getX(), getY(), getWidth(), getHeight(),
            false);

        g.setColor(Visual.LIST_PRIMARY_COLOR);
        g.setFont(Visual.MEDIUM_FONT);
        int y0 = getY() + getHeight() / 2 + g.getFont().getHeight() / 2;
        g.drawString("no guides", getX() + getWidth() / 2, y0, Graphics.BOTTOM
            | Graphics.HCENTER);
    }

    private void loadAccountAndGuides() {
        if (loadingAccount) {
            return;
        }
        loadingAccount = true;

        // For restoring guides user's account is needed
        int status = manager.getUserAndDeviceId(
            IAPClientPaymentManager.DEFAULT_AUTHENTICATION);
        if (status != IAPClientPaymentManager.SUCCESS) {
            showAlert("Authorization failure " + status,
                "Authorization failure", "Authorization process failed. "
                + getPaymentError(status));
            viewMaster.setPreviousView();
            loadingAccount = false;
        }
    }

    /**
     * @see IAPClientPaymentListener#userAndDeviceDataReceived(int, com.nokia.mid.payment.IAPClientUserAndDeviceData)
     */
    public final void userAndDeviceDataReceived(int status,
        IAPClientUserAndDeviceData ud) {
        if (status == OK) {
            account = ud.getAccount();
            loadGuides();
        }
        else {
            showAlert("Authorization listener failure " + status,
                "Authorization failure",
                "Authorization process failed. " + getPaymentError(status));
            viewMaster.setPreviousView();
        }
        loadingAccount = false;
    }

    private void loadGuides() {
        if (loadingGuides) {
            return;
        }
        loadingGuides = true;
        new NewGuidesOperation(new NewGuidesOperation.Listener() {

            public void guidesReceived(Vector newGuides) {
                if (newGuides == null) {
                    showAlert("Network failure", "Network failure",
                        "Connecting network failed.");
                    viewMaster.setPreviousView();
                    loadingGuides = false;
                }
                else {
                    waitingProductData = new Hashtable();
                    for (int i = 0, length = newGuides.size(); i < length; i++) {
                        Guide guide = (Guide) newGuides.elementAt(i);
                        if (!data.getGuides().contains(guide)) {
                            waitingProductData.put(guide.getId(), guide);
                            guide.setUrl(GUIDE_URL_PREFIX + guide.getId());
                            guide.setAccount(account);
                        }
                    }
                    loadProductData();
                }
            }
        }, NEW_GUIDES_URL, account).start();
    }

    private void loadProductData() {
        if (waitingProductData.isEmpty()) {
            waitingProductData = null;
            if (guides == null) {
                guides = new Vector();
                loadingGuides = false;
                if (isActive()) {
                    viewMaster.draw();
                }
            }
            return;
        }

        String[] productIds = new String[waitingProductData.size()];
        Enumeration e = waitingProductData.keys();
        for (int i = 0; e.hasMoreElements(); i++) {
            productIds[i] = ((String) e.nextElement());
        }
        int status = manager.getProductData(productIds);
        if (status != IAPClientPaymentManager.SUCCESS) {
            waitingProductData = null;
            showAlert("Metadata failure " + status, "Connection failure",
                "Loading new guides failed. " + getPaymentError(status));
            viewMaster.setPreviousView();
            loadingGuides = false;
        }
    }

    /**
     * @see IAPClientPaymentListener#productDataListReceived(int, com.nokia.mid.payment.IAPClientProductData[])
     */
    public final void productDataListReceived(int status,
        IAPClientProductData[] productDataList) {
        if (status == OK) {
            guides = new Vector();
            for (int i = 0, size = productDataList.length; i < size; i++) {
                IAPClientProductData productData = productDataList[i];
                if (productData.getProductId() != null) {
                    Guide guide = (Guide) waitingProductData.remove(productData.
                        getProductId());
                    String title = productData.getTitle();
                    if (title == null) {
                        title = productData.getShortDescription();
                    }
                    if (title == null) {
                        title = "unknown";
                    }
                    String price = productData.getPrice();
                    if (price == null) {
                        price = "unknown";
                    }
                    if (guide != null) {
                        guide.setCity(title);
                        guide.setPrice(price);
                        guides.addElement(guide);
                    }
                }
            }
            if (isActive()) {
                showGuides();
                viewMaster.draw();
            }
        }
        else {
            showAlert("Metadata listener failure " + status,
                "Connection failure",
                "Loading new guides failed. " + getPaymentError(status));
            viewMaster.setPreviousView();
        }
        waitingProductData = null;
        loadingGuides = false;
    }

    public final void restorableProductsReceived(int status,
        IAPClientProductData[] productDataList) {
    }

    public final void productDataReceived(int status,
        IAPClientProductData productData) {
    }

    private void selectGuide(int index) {
        if (purchasing != null) {
            return;
        }
        purchasing = (Guide) guides.elementAt(index);
        if (purchasing.isRestorable()) {
            // Restore guide from the backend
            guidePurchased("");
        }
        else {
            // Purchase guide using Nokia Store
            int status = manager.purchaseProduct(purchasing.getId(),
                IAPClientPaymentManager.FORCED_AUTOMATIC_RESTORATION);
            if (status != IAPClientPaymentManager.SUCCESS) {
                purchasing = null;
                showAlert("Purchase failure " + status, "Purchase failure", "Purchase process failed. "
                    + getPaymentError(status));
            }
        }
    }

    /**
     * @see IAPClientPaymentListener#purchaseCompleted(int, java.lang.String)
     */
    public final void purchaseCompleted(int status, String purchaseTicket) {
        switch (status) {
            case OK:
                guidePurchased(purchaseTicket);
                break;
            case RESTORABLE:
                guidePurchased(purchaseTicket == null ? "" : purchaseTicket);
                break;
            default:
                purchasing = null;
                showAlert("Purchase listener failure " + status,
                    "Purchase failure",
                    "Purchase process failed. " + getPaymentError(status));
                break;
        }
    }

    /**
     * @see IAPClientPaymentListener#restorationCompleted(int, java.lang.String)
     */
    public final void restorationCompleted(int status, String purchaseTicket) {
        switch (status) {
            case OK:
                guidePurchased(purchaseTicket);
                break;
            default:
                purchasing = null;
                showAlert("Restoration listener failure " + status,
                    "Restoration failure",
                    "Restoration failed. " + getPaymentError(status));
                break;
        }
    }

    private void guidePurchased(String purchaseTicket) {
        if (purchasing == null) {
            return;
        }
        purchasing.setPurchaseTicket(purchaseTicket);
        guides.removeElement(purchasing);
        data.getGuides().addElement(purchasing);
        data.setCurrentGuideIndex(data.getGuides().indexOf(purchasing));
        data.saveGuides();
        purchasing = null;
        viewMaster.showAttractionsView();
    }

    /**
     * @param code
     * @return Error string according to the code
     */
    public final String getPaymentError(int code) {
        switch (code) {
            case IAPClientPaymentListener.NO_PMT_METHODS:
                return "No payment methods are set up in your Nokia account. "
                    + "Please enable some of the methods to be able to "
                    + "proceed.";
            case IAPClientPaymentListener.OVI_SIGN_IN_FAILED:
                return "Cannot sign in to your Nokia Account. Please check "
                    + "your credentials.";
            case IAPClientPaymentListener.RESTORATION_DEVICE_LMT_EXCEEDED:
                return "Unfortunately the number of restorations allowed on "
                    + "the device has exceeded limit.";
            case IAPClientPaymentListener.RESTORATION_LMT_EXCEEDED:
                return "Unfortunately the number of restorations allowed for "
                    + "this item has exceeded limit.";
            case IAPClientPaymentListener.RESTORATION_FAILED:
            case IAPClientPaymentListener.RESTORATION_NOT_ALLOWED:
                return "It seems you haven't purchased the guide yet, and "
                    + "therefore restoration is not"
                    + "possible. Please, use the purchasing options.";
            case IAPClientPaymentListener.SMS_PMT_FAILED:
                return "Sending SMS has failed in operator payment.";
            default:
                return "There was a problem with Nokia Services. Please try "
                    + "again later. If the problem persists, please "
                    + "contact the application vendor.";
        }
    }

    public final void keyPressed(int keyCode) {
        handleKey(keyCode);
    }

    public final void keyRepeated(int keyCode) {
        handleKey(keyCode);
    }

    private void handleKey(int keyCode) {
        if (!isActive() || guides == null || guides.isEmpty()) {
            return;
        }
        switch (viewMaster.getGameAction(keyCode)) {
            case ViewMaster.UP:
                guidesList.focusUp();
                viewMaster.forceDraw();
                break;
            case ViewMaster.DOWN:
                guidesList.focusDown();
                viewMaster.forceDraw();
                break;
            case ViewMaster.FIRE:
                guidesList.select();
                break;
            default:
                break;
        }
    }

    public final void commandAction(Command cmd) {
        if (cmd == buyCmd) {
            if (guides != null && !guides.isEmpty()) {
                guidesList.select();
            }
        }
        else {
            super.commandAction(cmd);
        }
    }
}