Control.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.favouriteartists;

import java.io.IOException;
import java.util.Vector;
import java.util.Random;

import javax.microedition.lcdui.*;

import com.nokia.example.favouriteartists.tool.Log;

/**
 * This class controls the operation of the MIDlet, including command handling and view creation etc.
 */
public class Control implements CommandHandler, ImageProvider {

    // Constants
    /** Alert timeout in milliseconds. */
    private static final int ALERT_TIMEOUT = 3000;

    /** Enabled value for the JAD attributes. */
    public static final String ATTR_ENABLED = "true";

    /** Images for testing purposes */
    private static final String[] TEST_IMG_NAMES = {
        "/artist_1.png",
        "/artist_2.png",
        "/artist_3.png",
        "/artist_4.png",
    };

    /** Comments for testing. */
    private static final String[] COMMENTS = {
        "This artist sucks!",
        "This artist is bad.",
        "This artist is average.",
        "This artist is ok.",
        "This artist rocks!",
    };

    // Inner classes
    /**
     * Data structure class for image cache
     **/
    private class ImageData{
        /** Filename of the image, used as identifier */
        public String fileName;
        /** The image object */
        public Image image;
    }


    // Member data
    /** Reference of the midlet class.*/
    private FavouriteArtistsMIDlet mainApp;
    /** A <code>Display</code> object.*/
    private Display display;
    /** Favourite artists view, main view of the app. */
    private FavouriteArtistsView favouritesView;
    /** Image cache. */
    private Vector imageCache;
    /** Artist data. */
    private ArtistData[] artistDatas;
    /** Favourite data. */
    private Vector favouriteDatas;

    // Methods
    /**
     * Constructor.
     *
     * @param app reference to the main midlet.
     */
    public Control(FavouriteArtistsMIDlet app){
        mainApp = app;
    }

    /**
     * Separate initialization function for convenience.
     */
    public void initialize(){
        if (Log.TEST) Log.note("[Control#initialize]-->");
        display = Display.getDisplay(mainApp);

        imageCache = new Vector();

        // Hard-coded artist data for testing purposes
        artistDatas = new ArtistData[50];
        for (int i = 0; i < artistDatas.length; i++) {
             ArtistData artistData = new ArtistData();
             artistData.setName("Artist " + i);
             artistData.setGenres(new String[]{"Genre " + i});
             artistData.setShortDescription("Descr. " + i);
             artistData.setActiveMembers(new String[]{"Active members " + i});
             artistData.setFormerMembers(new String[]{"Former members " + i});
             artistData.setSignificantAlbums(new String[]{"Sgnf. albums " + i});
             artistData.setSignificantSongs(new String[]{"Sgnf. songs " + i});
             artistData.setSimilarArtists(new String[]{"Similar artists " + i});
             String imageFileName = TEST_IMG_NAMES[i % TEST_IMG_NAMES.length];
             artistData.setImageFileName(imageFileName);
             artistDatas[i] = artistData;
        }

        // Hard-coded favourite data for testing purposes
        favouriteDatas = new Vector(15);
        Random generator = new Random();
        generator.setSeed(System.currentTimeMillis());
        for (int i = 0; i < 15; i++) {
            FavouriteData favouriteData = new FavouriteData(artistDatas[i]);
            short rating = (short)generator.nextInt(4);
            favouriteData.setRating(rating);
            favouriteData.setComment(COMMENTS[rating]);
            favouriteDatas.addElement(favouriteData);
        }

        // Initialize main view
        try {
            favouritesView = new FavouriteArtistsView(this, display, this);
        } catch (FavouriteArtistsException e) {
            if (Log.TEST) Log.error("[Control#initialize] exception: " + e);
            return;
        }

        // Update main view
        updateFavouritesView(false);

        // Display main view
        display.setCurrent(favouritesView);
    }

    /**
     * Updates the favourites view with new data
     *
     * @param repaint If true, a repaint is initiated for the view.
     */
    private void updateFavouritesView(boolean repaint){
        if (Log.TEST) Log.note("[Control#updateFavouritesView]-->");
        FavouriteData[] favArray = new FavouriteData[favouriteDatas.size()];
        favouriteDatas.copyInto(favArray);
        favouritesView.updateView(favArray, repaint);
    }

    /**
     * Retrieves image from cache if already loaded or creates new image from file.
     *
     * @see ImageProvider#getImage(String)
     */
    public Image getImage(String imageFilename){
        if (Log.TEST) Log.note("[Control#getImage]-->");

        if(imageFilename == null){
            if (Log.TEST) Log.note("[Control#getImage] null param");
            return null;
        }

        // Search for image
        for(int i = 0; i < imageCache.size(); i++){
            ImageData imageData = (ImageData)imageCache.elementAt(i);
            if(imageFilename.equals(imageData.fileName)){
                if (Log.TEST) Log.note("[Control#getImage] image found from cahce");
                return imageData.image;
            }
        }
        // Create new image
        ImageData imgData = new ImageData();
        try{
            if (Log.TEST) Log.note("[Control#getImage] creating new image");
            imgData.image = Image.createImage("/" + imageFilename);
            imgData.fileName = imageFilename;
            imageCache.addElement(imgData);
        } catch (IOException e) {
          if (Log.TEST) Log.error("[Control#getImage] Exception: " + e.getMessage());
        }
        return imgData.image;
    }

    /**
     * Set a view to the current display.
     *
     * @param disp a view to set.
     */
    public void setCurrent(Displayable disp){
        if (display == null)
            display = Display.getDisplay(mainApp);
        display.setCurrent(disp);
    }

    /**
     * @see CommandListener#commandAction(Command, Displayable)
     */
    public void commandAction(Command c, Displayable d) {
        if (Log.TEST) Log.note("[Control#commandAction]--> CommandListener");

        ActionCommand ac = (ActionCommand) c;
        short actionId = ac.getActionId();
        handleAction(actionId, null, d);
    }

    /**
     * @see ItemCommandListener#commandAction(Command, Item)
     */
    public void commandAction(Command c, Item item) {
        if (Log.TEST) Log.note("[Control#commandAction]--> ItemCommandListener");

        ActionCommand ac = (ActionCommand) c;
        short actionId = ac.getActionId();
        handleAction(actionId, item, null);
    }

    /**
     * @see com.nokia.example.favouriteartists.CommandHandler#handleAction(short, Item, Displayable)
     */
    public synchronized void handleAction(short actionId, Item item, Displayable view) {

        if (Log.TEST) Log.note("[Control#handleAction]-->");
        switch(actionId) {

            case Actions.EXIT_MIDLET:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.EXIT_MIDLET");
                exitMidlet();
                break;
            }
            case Actions.BACK:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.BACK");
                // Favourites view is always the view to go back to
                displayFavouritesView();
                break;
            }
            case Actions.SHOW_RATING:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.SHOW_RATING");
                // Get selected item from the view. Should be valid during action handling.
                FavouriteData favData = favouritesView.getSelectedItem();
                try {
                    RatingView ratingView = new RatingView(favData, this, this);
                    display.setCurrent(ratingView);
                } catch (FavouriteArtistsException e) {
                    displayNote(null, "Operation failed!", AlertType.ERROR);
                }
                break;
            }
            case Actions.RATING_DONE:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.RATING_DONE");
                // Rating is done, get the fav data that needs to be changed.
                RatingView ratingView = (RatingView)view;
                FavouriteData favData = ratingView.getFavouriteData();
                // Item in the Favourites view has reference to same object, so we can
                // modify it directly.
                favData.setName(ratingView.getName());
                favData.setRating(ratingView.getRatingValue());
                favData.setComment(ratingView.getComment());
                displayFavouritesView();
                break;
            }
            case Actions.SHOW_ADD_FAVOURITE:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.SHOW_ADD_FAVOURITE");
                // Open add fav view
                AddFavouriteView addFavView = null;
                try {
                    addFavView = new AddFavouriteView(this, display, this, artistDatas);
                } catch (FavouriteArtistsException e) {
                    displayNote(null, "Operation failed!", AlertType.ERROR);
                    break;
                }
                display.setCurrent(addFavView);
                break;
            }
            case Actions.ADD_FAVOURITE:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.ADD_FAVOURITE");
                AddFavouriteView addFavView = (AddFavouriteView)view;
                // Get selected artist
                ArtistData artistData = addFavView.getSelectedItem().getData();
                // Create a new favourite from the artist
                FavouriteData favData = new FavouriteData(artistData);
                // Set some default comment for the favourite, for testing purposes.
                favData.setComment(COMMENTS[favData.getRating()]);
                favouriteDatas.addElement(favData);
                // Update favourites view, needed because amount of items has changed.
                updateFavouritesView(false);
                displayFavouritesView();
                break;
            }

            case Actions.REMOVE_FAVOURITE:{
                if (Log.TEST) Log.note("[Control#handleAction] Actions.REMOVE_FAVOURITE");
                RatingView ratingView = (RatingView)view;
                // Get the item and remove it
                FavouriteData favToRemove = ratingView.getFavouriteData();
                favouriteDatas.removeElement(favToRemove);
                // Update favourites view, needed because amount of items has changed.
                updateFavouritesView(false);
                displayFavouritesView();
                break;
            }
            case Actions.ARRANGE_FAVOURITES:{
                arrangeFavourites();
                updateFavouritesView(true);
                break;
            }
        }
    }


    /**
     * Show the main view.
     */
    private void displayFavouritesView() {
        if (Log.TEST) Log.note("[Control#displayFavouritesView]-->");
        display.setCurrent(favouritesView);
    }

    /**
     * Called when the user has chosen to exit the MIDlet or when
     * the MIDlet receives a <code>destroyApp()</code> call.
     */
   public final void exitMidlet() {
        if (Log.TEST) Log.note("[Control#exitMidlet]-->");
        mainApp.closeLogging();
        mainApp.notifyDestroyed();
    }

   /**
     * Displays an alert that times out after {@link #ALERT_TIMEOUT} milliseconds.
     * The shown alert has no command and it will not trigger any action.
     */
    public void displayNote(String title, String msg, AlertType type) {
        Alert alert = new Alert(title, msg, null, type);
        alert.setTimeout(ALERT_TIMEOUT);
        displayAlert(alert);
    }

    /**
     * Display an alert.
     *
     * @param alert The alert to display.
     */
    private void displayAlert(Alert alert) {

        try {
            display.setCurrent(alert, favouritesView);
        } catch (Exception e) {
            if (Log.TEST) Log.error("[Control#displayAlert] error:", e);
        }
    }

    /**
     * Arrange favourites by rating.
     */
    private void arrangeFavourites(){

        boolean swapped;
        do{
            swapped = false;
            for( int i = 0; i <= favouriteDatas.size() - 2; i++){
                FavouriteData element1 = (FavouriteData)favouriteDatas.elementAt(i);
                FavouriteData element2 = (FavouriteData)favouriteDatas.elementAt(i + 1);
                if( element1.rating < element2.rating ){
                    favouriteDatas.setElementAt(element2, i);
                    favouriteDatas.setElementAt(element1, i + 1);
                    swapped = true;
                }
            }
        } while (swapped == true);
    }
}