/* * TimelineCanvas.java * * Copyright (C) 2005-2009 Tommi Laukkanen * http://www.substanceofcode.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.substanceofcode.twitter.views; import com.substanceofcode.infrastructure.Device; import com.substanceofcode.twitter.TwitterController; import com.substanceofcode.twitter.model.Status; import com.substanceofcode.utils.Log; import java.util.Calendar; import java.util.Enumeration; import java.util.Vector; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.game.GameCanvas; /** * TimelineCanvas * * @author Tommi Laukkanen (tlaukkanen at gmail dot com) */ public class TimelineCanvas extends Canvas { private TwitterController controller; private Vector statuses; private StatusList statusList; private boolean drawNextPageLink; private TabBar menuBar; private Menu menu; private Menu statusMenu; private Menu mediaSourceMenu; private int verticalScroll; private Point pointerPressedPoint = new Point(0, 0); private int lastY; /** Last touch coordinates */ private int screenWidth; /** Screen width to identify the screen rotation */ private String debug = "x"; private int boxWidth = 20, boxHeight = 20; private static final Font LABEL_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM); private static final int HOME_TAB = 3; private static final int RETWEETS_OF_ME_TAB = 2; /** * Creates a new instance of TimelineCanvas * @param controller Application controller */ public TimelineCanvas(TwitterController controller) { this.controller = controller; setFullScreenMode(true); drawNextPageLink = true; /** Menu bar tabs */ String[] labels = { "Archive", "Replies", "Retweets", "Home", "Direct", "Favorites", "Following", "Public"}; menuBar = new TabBar(HOME_TAB, labels); /** Main menu */ String[] menuLabels = { "Update status", "Send media", "Reload tweets", "Search tweets", "Lists", "Settings", "About", "Exit", "Minimize"}; menu = new Menu(menuLabels, null, getWidth(), getHeight()); /** Photo source */ String[] photoSourceLabels = { "Camera", "Photo from file", "Video from file", "Cancel"}; mediaSourceMenu = new Menu(photoSourceLabels, null, getWidth(), getHeight()); mediaSourceMenu.setTitle("Select source"); /** Status menu */ String[] statusMenuLabels = { "Open in browser", "Open link in browser", "Reply", "Retweet", "Mark as favorite", "Send direct message", "Follow", "Translate to English"}; statusMenu = new Menu(statusMenuLabels, null, getWidth(), getHeight()); statusMenu.setTitle("Status menu"); /** Status list control */ statusList = new StatusList(getWidth(), getHeight()); statusList.setSize(getWidth(),getHeight()); verticalScroll = 0; screenWidth = getWidth(); } public void showDrawNextPageLink(boolean show) { drawNextPageLink = show; } public void resetMenus() { menu.deactivate(); statusMenu.deactivate(); mediaSourceMenu.deactivate(); } public void resetScrolling() { verticalScroll = 0; } public void setTimeline(Vector friendsTimeline) { if(friendsTimeline.isEmpty()) { this.statuses = new Vector(); this.statuses.addElement( new Status("Twim", "Sorry... No statuses to display", Calendar.getInstance().getTime(), "") ); } this.statuses = friendsTimeline; } public void paint(Graphics g) { /** Check for screen rotation change */ if(screenWidth != getWidth()) { menu.setSize(getWidth(), getHeight()); statusMenu.setSize(getWidth(), getHeight()); mediaSourceMenu.setSize(getWidth(), getHeight()); statusList.setSize(getWidth(),getHeight()); screenWidth = getWidth(); if(statuses!=null) { Enumeration en = statuses.elements(); while(en.hasMoreElements()) { Status status = (Status)en.nextElement(); status.clearTextLines(); } } } g.setColor(Theme.COLOR_BACKGROUND); g.fillRect(0, 0, getWidth(), getHeight()); if( menu.isActive()==false && statusMenu.isActive()==false && mediaSourceMenu.isActive()==false) { boolean drawSelectionBox = menuBar.isSelectedActive(); statusList.draw( g, statuses, menuBar.getHeight() + verticalScroll + Theme.FONT_TEXT.getHeight()/2, drawSelectionBox, drawNextPageLink); menuBar.draw(g, 0, getWidth()); if(Device.isTouch()) { drawMenuButton(g); } } else if(menu.isActive()) { menu.draw(g); } else if(statusMenu.isActive()) { statusMenu.draw(g); } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.draw(g); } //g.drawString(debug, 0, 40, Graphics.LEFT|Graphics.BOTTOM); } private void handleTabChange() { verticalScroll = 0; int tabIndex = menuBar.getSelectedTabIndex(); if(tabIndex==0) { /** Archive selected */ controller.showArchiveTimeline(false); } else if(tabIndex==1) { /** Responses selected */ controller.showResponsesTimeline(); } else if(tabIndex==2) { /** Retweets selected */ controller.showRetweetsOfMeTimeline(false); } else if(tabIndex==3) { /** Recent selected */ controller.showHomeTimeline(false); } else if(tabIndex==4) { /** Direct messages */ controller.showDirectMessages(); } else if(tabIndex==5) { /** Favorites */ controller.showFavouriteTimeline(); } else if(tabIndex==6) { /** Friends */ controller.showFriends(); } else if(tabIndex==7) { /** Public selected */ controller.showPublicTimeline(); } } /** Handle repeated key presses. */ protected void keyRepeated(int keyCode) { handleUpAndDownKeys(keyCode); repaint(); } private void handleUpAndDownKeys(int keyCode) { Log.debug("handle up/down"); int gameAction = this.getGameAction(keyCode); if(gameAction == GameCanvas.UP) { menuBar.resetSelectedTab(); if(menu.isActive()) { menu.selectPrevious(); } else if(statusMenu.isActive()) { statusMenu.selectPrevious(); } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.selectPrevious(); } else { verticalScroll += getHeight()/6; if(verticalScroll>0) { verticalScroll = 0; } } } else if(gameAction == GameCanvas.DOWN) { menuBar.resetSelectedTab(); if(menu.isActive()) { menu.selectNext(); } else if(statusMenu.isActive()) { statusMenu.selectNext(); } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.selectNext(); } else { verticalScroll -= getHeight()/6; } } } public void activateMenuItem() { int selectedIndex = menu.getSelectedIndex(); if(selectedIndex==0) { controller.showStatusView(""); } else if(selectedIndex==1) { /** Media service */ menu.deactivate(); mediaSourceMenu.activate(); repaint(); } else if(selectedIndex==2) { /** Reload tweets */ controller.clearTimelines(); handleTabChange(); } else if(selectedIndex==3) { controller.showSearchForm(); } else if(selectedIndex==4) { controller.showLists(); } else if(selectedIndex==5) { controller.showSettingsForm(); } else if(selectedIndex==6) { controller.about(); } else if(selectedIndex==7) { controller.exit(); } else if(selectedIndex==8) { controller.minimize(); } } public void activateMediaSourceMenuItem() { if(mediaSourceMenu.getSelectedIndex()==0) { /** Camera */ controller.showCamera(); } else if(mediaSourceMenu.getSelectedIndex()==1){ /** Photo from file */ controller.showPhotoBrowser(); } else if(mediaSourceMenu.getSelectedIndex()==2){ /** Video from file */ controller.showVideoBrowser(); } else { /** Cancel */ mediaSourceMenu.deactivate(); repaint(); } } public void activateStatusMenuItem() { int selectedIndex = statusMenu.getSelectedIndex(); Status selectedStatus = statusList.getSelected(); if(selectedIndex==0) { /** Open tweet in browser */ if(selectedStatus!=null) { selectedStatus.openInBrowser(controller.getMIDlet()); return; } } else if(selectedIndex==1) { /** Open tweet link in browser */ if(selectedStatus!=null) { selectedStatus.openIncludedLink(controller.getMIDlet()); return; } } else if(selectedIndex==2) { /** Reply to tweet */ if(selectedStatus!=null) { if(selectedStatus.isDirect()) { controller.showStatusView("d " + selectedStatus.getScreenName() + " "); } else { controller.showStatusView("@" + selectedStatus.getScreenName() + " "); } } } else if(selectedIndex==3) { /** Retweet */ if(selectedStatus!=null) { controller.showStatusView("RT @" + selectedStatus.getScreenName() + " \"" + selectedStatus.getText() + "\""); } } else if(selectedIndex==4) { /** Mark as favorite */ if(selectedStatus!=null) { controller.toggleFavorite(selectedStatus); } } else if(selectedIndex==5) { /** Send direct message */ if(selectedStatus!=null) { controller.showStatusView("d " + selectedStatus.getScreenName() + " "); } }else if(selectedIndex==6) { /** Follow/Unfollow */ if(selectedStatus!=null) { controller.toggleFollow(selectedStatus); } }else if(selectedIndex==7) { /** Translate to English */ if(selectedStatus!=null) { controller.translateToEnglish(selectedStatus); } } } public void keyPressed(int keyCode) { int gameAction = this.getGameAction(keyCode); String keyName = this.getKeyName(keyCode); Log.debug("key: " + keyName); if(gameAction == Canvas.LEFT) { menuBar.selectPreviousTab(); //handleTabChange(); repaint(); return; } else if(gameAction == Canvas.RIGHT) { menuBar.selectNextTab(); //handleTabChange(); repaint(); return; } else if(gameAction == Canvas.FIRE || keyName.toUpperCase().startsWith("ENTER") || (keyName.toUpperCase().startsWith("SPACE") && (menu.isActive() || statusMenu.isActive() || mediaSourceMenu.isActive()) ) ) { if(menuBar.isSelectedActive()==false) { menuBar.activateSelectedTab(); handleTabChange(); repaint(); return; } if(menu.isActive()) { menu.deactivate(); activateMenuItem(); return; } else if(statusMenu.isActive()) { statusMenu.deactivate(); activateStatusMenuItem(); return; } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.deactivate(); activateMediaSourceMenuItem(); return; } else if(statusList.getSelected()!=null){ Status selectedStatus = statusList.getSelected(); if(selectedStatus.getId().equals("page")) { loadNextPage(); return; } if(selectedStatus.isFavorite()) { statusMenu.setLabel(4, "Unfavorite"); } else { statusMenu.setLabel(4, "Mark as favorite"); } if(selectedStatus.isFollowing()) { statusMenu.setLabel(6, "Unfollow user"); } else { statusMenu.setLabel(6, "Follow user"); } statusMenu.activate(); } } else if( (keyName.indexOf("SOFT")>=0 && keyName.indexOf("1")>0) || (Device.isNokia() && keyCode==-6) || keyCode == TimelineCanvas.KEY_STAR || keyCode == Canvas.KEY_NUM0 || keyCode == ' ') { /** Left soft key pressed */ if(statusMenu.isActive()) { statusMenu.deactivate(); } else if(menu.isActive()) { menu.deactivate(); //activateMenuItem(); repaint(); return; } else { menu.activate(); } } else if( (keyName.indexOf("SOFT")>=0 && keyName.indexOf("2")>0) || (Device.isNokia() && keyCode==-7) || keyCode == TimelineCanvas.KEY_POUND || keyCode == Canvas.KEY_NUM0 || keyCode == ' ') { /** Right soft key pressed */ if(statusMenu.isActive()) { statusMenu.deactivate(); } else if(menu.isActive()) { menu.deactivate(); //activateMenuItem(); repaint(); return; } else { menu.activate(); } } handleUpAndDownKeys(keyCode); repaint(); } /** * Handle touch screen press. * @param x coordinate * @param y coordinate */ protected void pointerPressed(int x, int y) { super.pointerPressed(x, y); Device.setTouch(true); if(menu.isActive()) { menu.selectWithPointer(x, y, true); } else if(statusMenu.isActive()) { statusMenu.selectWithPointer(x, y, true); } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.selectWithPointer(x, y, true); } else { pointerPressedPoint.x = x; pointerPressedPoint.y = y; lastY = y; debug = "(" + x + "," + y + ")"; if(y>getHeight()-boxHeight && x<getWidth()/2) { menu.activate(); } if(y>getHeight()-boxHeight && x>getWidth()/2) { statusMenu.activate(); } } repaint(); } /** * Handle touch screen drag * @param x coordinate * @param y coordinate */ protected void pointerDragged(int x, int y) { super.pointerDragged(x, y); if(menu.isActive()) { menu.selectWithPointer(x, y, false); } else if(statusMenu.isActive()) { statusMenu.selectWithPointer(x, y, false); } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.selectWithPointer(x, y, false); } else { if(y<LABEL_FONT.getHeight()*2) { int tabWidth = getWidth()/7; int selectedTabIndex = pointerPressedPoint.x/tabWidth; menuBar.selectTab(selectedTabIndex); pointerPressedPoint.x = x; pointerPressedPoint.y = y; } else { if(lastY>0) { int dy = y - lastY; verticalScroll += dy; lastY = y; } } } repaint(); } /** * Handle touch screen press release event * @param x coordinate * @param y coordinate */ protected void pointerReleased(int x, int y) { if(menu.isActive()) { menu.selectWithPointer(x, y, false); if(menu.getSelectedLabel()!=null && menu.getSelectedLabel().length()>0) { Log.debug("Selected menu item: " + menu.getSelectedLabel()); menu.deactivate(); activateMenuItem(); } } else if(statusMenu.isActive()) { statusMenu.selectWithPointer(x, y, false); if(statusMenu.getSelectedLabel()!=null && statusMenu.getSelectedLabel().length()>0) { statusMenu.deactivate(); activateStatusMenuItem(); } } else if(mediaSourceMenu.isActive()) { mediaSourceMenu.selectWithPointer(x, y, false); if(mediaSourceMenu.getSelectedLabel()!=null && mediaSourceMenu.getSelectedLabel().length()>0) { mediaSourceMenu.deactivate(); activateMediaSourceMenuItem(); } } else { if(!menuBar.isSelectedActive()) { menuBar.activateSelectedTab(); handleTabChange(); return; } else { } if(lastY>0) { int dy = y - lastY; verticalScroll += dy; lastY = y; } } repaint(); } private void drawMenuButton(Graphics g) { g.setFont(LABEL_FONT); int wi = LABEL_FONT.stringWidth("Menu"); int wii = LABEL_FONT.stringWidth("Item"); int fontHeight = LABEL_FONT.getHeight(); boxWidth = wi + fontHeight; boxHeight = fontHeight*2; g.setColor(0xaaaaaa); g.fillRect(0, getHeight()-boxHeight, getWidth(), boxHeight); g.setColor(0x999999); g.drawLine(getWidth()/2, getHeight()-boxHeight+2, getWidth()/2, getHeight()-4); g.setColor(0x444444); g.drawLine(0, getHeight()-boxHeight, getWidth(), getHeight()-boxHeight); g.drawString("Menu", boxWidth/2-wi/2, getHeight()-boxHeight/2+fontHeight/2, Graphics.LEFT|Graphics.BOTTOM); g.drawString("Item", getWidth()-boxWidth+boxWidth/2-wii/2, getHeight()-boxHeight/2+fontHeight/2, Graphics.LEFT|Graphics.BOTTOM); } public void resetMenuTab() { menuBar.selectNothing(); } private void loadNextPage() { controller.loadNextPage(); } public void selectHomeTab(boolean resetVerticalScroll) { menuBar.selectTab( HOME_TAB ); menuBar.activateSelectedTab(); if(resetVerticalScroll) { verticalScroll = 0; } } public void selectRetweetsOfMeTab(boolean resetVerticalScroll) { menuBar.selectTab( RETWEETS_OF_ME_TAB ); menuBar.activateSelectedTab(); if(resetVerticalScroll) { verticalScroll = 0; } } }