DetailCanvas.java

/**
 * Copyright (c) 2012-2013 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.picasaviewer.ui;

import com.nokia.example.picasaviewer.PicasaViewer;
import java.util.Vector;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import org.tantalum.Task;
import org.tantalum.TimeoutException;
import org.tantalum.net.StaticWebCache;
import org.tantalum.util.L;

import com.nokia.example.picasaviewer.common.PicasaImageObject;
import com.nokia.example.picasaviewer.common.PicasaStorage;
import com.nokia.example.picasaviewer.util.ViewManager;

/**
 * The class displaying the larger image, title and photographer.
 */
public final class DetailCanvas 
    extends GestureCanvas
{
    // Constants
    private static final int PADDING = 5;
    private static final double R = 12;

    // Members
    private final Command backCommand = new Command("Back", Command.BACK, 0);
    private volatile Image image = null;
    private Vector titleLines = null;
    private final int width;
    private int fontHeight = 0;

    /**
     * Constructor.
     * @param viewManager
     */
    public DetailCanvas(ViewManager viewManager) {
        super(viewManager);
        
        yC = 50.0;
        width = getWidth();
        titleLines = new Vector();
        fontHeight = Font.getDefaultFont().getHeight();
        
        setFullScreenMode(true);
        
        // This view always has a back command as category bar is not shown
        addCommand(backCommand);
    }

    /**
     * @see javax.microedition.lcdui.CommandListener#commandAction(
     * javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
     */
    public void commandAction(Command command, Displayable displayable) {
        if (command.getCommandType() == Command.BACK) {
            viewManager.goBack();
        }
    }

    /**
     * Resets the scroll position and hides category bar when the view is shown.
     * @see javax.microedition.lcdui.Canvas#showNotify()
     */
    public void showNotify() {
        scrollY = 0;
        
        if (viewManager.supportsCategoryBar()) {
            viewManager.getCategoryBarHandler().setVisibility(false);
        }
        
        top = -calculateHeight() + getHeight();
    }

    /**
     * @see GestureCanvas#hideNotify()
     */
    public void hideNotify() {
        image = null;
        stopSpinner();
        super.hideNotify();
        titleLines.removeAllElements();
    }

    /**
     * @see GestureCanvas#sizeChanged(int, int)
     */
    public void sizeChanged(int w, int h) {
        super.sizeChanged(w, h);
    }

    /**
     * @see javax.microedition.lcdui.Canvas#paint(javax.microedition.lcdui.Graphics)
     */
    public void paint(final Graphics graphics) {
        checkThatScrollDoesNotExceedBoundaries();
        
        final PicasaImageObject selectedImage = PicasaStorage.getSelectedImage();
        
        if (titleLines.isEmpty()) {
            splitToLines(titleLines, selectedImage.title,
                Font.getDefaultFont(), width - 2 * PADDING);
        }
        
        //#debug
        L.i("Paint DetailCanvas", selectedImage.imageUrl);
        
        graphics.setColor(0x000000);
        graphics.fillRect(0, 0, getWidth(), getHeight());
        graphics.setColor(0xffffff);
        
        boolean startingSpin = false;
        
        // If we do not have the image and we are not loading it, start loading it
        if (image == null && !isSpinning()) {
            startSpinner();
            startingSpin = true;
            
            try {
                PicasaStorage.imageCache.getAsync(
                    selectedImage.imageUrl,
                    Task.HIGH_PRIORITY,
                    StaticWebCache.GET_ANYWHERE,
                    new Task() {
                        public Object exec(final Object in) {
                            if (in != null
                                && selectedImage == PicasaStorage.getSelectedImage())
                            {
                                image = (Image) in;
                                stopSpinner();
                                top = -calculateHeight() + getHeight();
                            }
                            
                            return in;
                        }
                    }).join(100);
            }
            catch (TimeoutException ex) {
                // Normal for slow load
            }
            catch (Exception ex) {
                //#debug
                L.e("Can not join image load", selectedImage.imageUrl, ex);
            }
        }
        
        if (isSpinning()) {
            if (!startingSpin) {
                drawSpinner(graphics);
            }
        }
        else if (image != null) {
            // Done, draw image
            graphics.drawImage(image, getWidth() / 2, scrollY,
                Graphics.TOP | Graphics.HCENTER);
        }
        
        int textY = (image == null) ? 
                (int) yC + ((int) R) << 1 : 
                image.getHeight() + scrollY;
        
        graphics.setColor(0xFFFFFF);
        
        // Paint the description text
        for (int i = 0; i < titleLines.size(); i++) {
            graphics.drawString((String) titleLines.elementAt(i),
                PADDING, textY,
                Graphics.LEFT | Graphics.TOP);
            textY += fontHeight;
        }
        
        // Paint the name of the author
        graphics.drawString(selectedImage.author, PADDING, textY,
            Graphics.LEFT | Graphics.TOP);
        
        drawBackIcon(graphics);
    }

    /**
     * @see GestureCanvas#gestureTap(int, int)
     */
    public boolean gestureTap(int startX, int startY) {
        boolean tapWasHandledAndConsumed = false;
        
        if (!PicasaViewer.HAS_ONE_KEY_BACK) {
            viewManager.goBack();
            tapWasHandledAndConsumed = true;
        }
        
        return tapWasHandledAndConsumed;
    }

    /**
     * Calculates the height of the view for the scrolling boundary.
     * @return The height of the view.
     */
    private int calculateHeight() {
        int height = image == null ? 0 : image.getHeight();
        height += titleLines == null ? 0 : titleLines.size() * fontHeight;
        height += fontHeight; // Author
        height += PADDING * 2;
        
        if (height < getHeight()) {
            height = getHeight();
        }
        
        System.out.println("DetailsCanvas.calculateHeight(): " + height);
        return height;
    }

    /**
     * Split a string in to several lines of text which will display within a
     * maximum width.
     * 
     * @param vector
     * @param text
     * @param font
     * @param maxWidth 
     */
    private void splitToLines(
            final Vector vector,
            final String text,
            final Font font,
            final int maxWidth)
    {
        int lastSpace = 0;
        
        for (int i = 0; i < text.length(); i++) {
            if (text.charAt(i) == ' ') {
                lastSpace = i;
            }
            
            final int len = font.stringWidth(text.substring(0, i));
            
            if (len > maxWidth) {
                vector.addElement(text.substring(0, lastSpace + 1).trim());
                splitToLines(vector,
                             text.substring(lastSpace + 1),
                             font,
                             maxWidth);
                return;
            }
        }
        
        vector.addElement(text.trim());
    } 
}