Home | History | Annotate | Download | only in replicaisland
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.replica.replicaisland;
     18 
     19 import java.util.ArrayList;
     20 
     21 import android.app.Activity;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.graphics.Canvas;
     25 import android.graphics.Paint;
     26 import android.graphics.drawable.AnimationDrawable;
     27 import android.os.Bundle;
     28 import android.os.SystemClock;
     29 import android.text.SpannableStringBuilder;
     30 import android.text.TextUtils;
     31 import android.util.AttributeSet;
     32 import android.view.MotionEvent;
     33 import android.view.View;
     34 import android.widget.ImageView;
     35 import android.widget.TextView;
     36 
     37 import com.replica.replicaisland.ConversationUtils.Conversation;
     38 import com.replica.replicaisland.ConversationUtils.ConversationPage;
     39 
     40 public class ConversationDialogActivity extends Activity {
     41 
     42     private final static float TEXT_CHARACTER_DELAY = 0.1f;
     43     private final static int TEXT_CHARACTER_DELAY_MS = (int)(TEXT_CHARACTER_DELAY * 1000);
     44     private ConversationUtils.Conversation mConversation;
     45     private ArrayList<ConversationUtils.ConversationPage> mPages;
     46     private int mCurrentPage;
     47 
     48     private ImageView mOkArrow;
     49     private AnimationDrawable mOkAnimation;
     50 
     51     @Override
     52     protected void onCreate(Bundle savedInstanceState) {
     53         super.onCreate(savedInstanceState);
     54         setContentView(R.layout.conversation_dialog);
     55 
     56         mOkArrow = (ImageView)findViewById(R.id.ok);
     57         mOkArrow.setBackgroundResource(R.anim.ui_button);
     58 		mOkAnimation = (AnimationDrawable) mOkArrow.getBackground();
     59 		mOkArrow.setVisibility(View.INVISIBLE);
     60 
     61         final Intent callingIntent = getIntent();
     62         final int levelRow = callingIntent.getIntExtra("levelRow", -1);
     63         final int levelIndex = callingIntent.getIntExtra("levelIndex", -1);
     64         final int index = callingIntent.getIntExtra("index", -1);
     65         final int character = callingIntent.getIntExtra("character", 1);
     66 
     67         mPages = null;
     68 
     69         // LevelTree.get(mLevelRow, mLevelIndex).dialogResources.character2Entry.get(index)
     70         if (levelRow != -1 && levelIndex != -1 && index != -1) {
     71         	if (character == 1) {
     72         		mConversation = LevelTree.get(levelRow, levelIndex).dialogResources.character1Conversations.get(index);
     73         	} else {
     74         		mConversation = LevelTree.get(levelRow, levelIndex).dialogResources.character2Conversations.get(index);
     75         	}
     76         	TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
     77         	tv.setParentActivity(this);
     78 
     79         } else {
     80         	// bail
     81         	finish();
     82         }
     83     }
     84 
     85     private void formatPages(Conversation conversation, TextView textView) {
     86 		Paint paint = new Paint();
     87         final int maxWidth = textView.getWidth();
     88         final int maxHeight = textView.getHeight();
     89         paint.setTextSize(textView.getTextSize());
     90         paint.setTypeface(textView.getTypeface());
     91 
     92         for (int page = conversation.pages.size() - 1; page >= 0 ; page--) {
     93         	ConversationUtils.ConversationPage currentPage = conversation.pages.get(page);
     94         	CharSequence text = currentPage.text;
     95         	// Iterate line by line through the text.  Add \n if it gets too wide,
     96         	// and split into a new page if it gets too long.
     97         	int currentOffset = 0;
     98         	int textLength = text.length();
     99         	SpannableStringBuilder spannedText = new SpannableStringBuilder(text);
    100         	int lineCount = 0;
    101         	final float fontHeight = -paint.ascent() + paint.descent();
    102         	final int maxLinesPerPage = (int)(maxHeight / fontHeight);
    103         	CharSequence newline = "\n";
    104         	int addedPages = 0;
    105         	int lastPageStart = 0;
    106         	do {
    107 	        	int fittingChars = paint.breakText(text, currentOffset, textLength, true, maxWidth, null);
    108 
    109 	        	if (currentOffset + fittingChars < textLength) {
    110 	        		fittingChars -= 2;
    111 	        		// Text doesn't fit on the line.  Insert a return after the last space.
    112 	        		int lastSpace = TextUtils.lastIndexOf(text, ' ', currentOffset + fittingChars - 1);
    113 	        		if (lastSpace == -1) {
    114 	        			// No spaces, just split at the last character.
    115 	        			lastSpace = currentOffset + fittingChars - 1;
    116 	        		}
    117 	        		spannedText.replace(lastSpace, lastSpace + 1, newline, 0, 1);
    118 	        		lineCount++;
    119 	        		currentOffset = lastSpace + 1;
    120 	        	} else {
    121 	        		lineCount++;
    122 	        		currentOffset = textLength;
    123 	        	}
    124 
    125 	        	if (lineCount >= maxLinesPerPage || currentOffset >= textLength) {
    126         			lineCount = 0;
    127         			if (addedPages == 0) {
    128         				// overwrite the original page
    129         				currentPage.text = spannedText.subSequence(lastPageStart, currentOffset);
    130         			} else {
    131         				// split into a new page
    132 	        			ConversationPage newPage = new ConversationPage();
    133 		                newPage.imageResource = currentPage.imageResource;
    134 		                newPage.text = spannedText.subSequence(lastPageStart, currentOffset);
    135 		                newPage.title = currentPage.title;
    136 		                conversation.pages.add(page + addedPages, newPage);
    137         			}
    138         			lastPageStart = currentOffset;
    139         			addedPages++;
    140         		}
    141         	} while (currentOffset < textLength);
    142 
    143 
    144         }
    145 
    146         // Holy crap we did a lot of allocation there.
    147         Runtime.getRuntime().gc();
    148 	}
    149 
    150 	@Override
    151     public boolean onTouchEvent(MotionEvent event) {
    152         if (event.getAction() == MotionEvent.ACTION_UP) {
    153             TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
    154 
    155             if (tv.getRemainingTime() > 0) {
    156                 tv.snapToEnd();
    157             } else {
    158                 mCurrentPage++;
    159                 if (mCurrentPage < mPages.size()) {
    160                     showPage(mPages.get(mCurrentPage));
    161                 } else {
    162                     finish();
    163                 }
    164             }
    165         }
    166         // Sleep so that the main thread doesn't get flooded with UI events.
    167         try {
    168             Thread.sleep(32);
    169         } catch (InterruptedException e) {
    170             // No big deal if this sleep is interrupted.
    171         }
    172         return true;
    173     }
    174 
    175     protected void showPage(ConversationUtils.ConversationPage page) {
    176         TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
    177         tv.setTypewriterText(page.text);
    178 
    179 		mOkArrow.setVisibility(View.INVISIBLE);
    180 		mOkAnimation.start();
    181 
    182 		tv.setOkArrow(mOkArrow);
    183 
    184         ImageView image = (ImageView)findViewById(R.id.speaker);
    185         if (page.imageResource != 0) {
    186         	image.setImageResource(page.imageResource);
    187         	image.setVisibility(View.VISIBLE);
    188         } else {
    189         	image.setVisibility(View.GONE);
    190         }
    191 
    192         TextView title = (TextView)findViewById(R.id.speakername);
    193         if (page.title != null) {
    194             title.setText(page.title);
    195             title.setVisibility(View.VISIBLE);
    196         } else {
    197         	title.setVisibility(View.GONE);
    198         }
    199 
    200     }
    201 
    202     public void processText() {
    203     	if (!mConversation.splittingComplete) {
    204     		TextView textView = (TextView)findViewById(R.id.typewritertext);
    205     		formatPages(mConversation, textView);
    206     		mConversation.splittingComplete = true;
    207     	}
    208 
    209     	if (mPages == null) {
    210 	    	mPages = mConversation.pages;
    211 	        showPage(mPages.get(0));
    212 
    213 	        mCurrentPage = 0;
    214     	}
    215     }
    216 
    217 
    218     public static class TypewriterTextView extends TextView {
    219         private int mCurrentCharacter;
    220         private long mLastTime;
    221         private CharSequence mText;
    222         private View mOkArrow;
    223         private ConversationDialogActivity mParentActivity;  // This really sucks.
    224 
    225         public TypewriterTextView(Context context) {
    226             super(context);
    227         }
    228 
    229         public TypewriterTextView(Context context, AttributeSet attrs) {
    230             super(context, attrs);
    231         }
    232 
    233         public TypewriterTextView(Context context, AttributeSet attrs, int defStyle) {
    234             super(context, attrs, defStyle);
    235         }
    236 
    237         public void setParentActivity(ConversationDialogActivity parent) {
    238         	mParentActivity = parent;
    239         }
    240 
    241         public void setTypewriterText(CharSequence text) {
    242             mText = text;
    243             mCurrentCharacter = 0;
    244             mLastTime = 0;
    245             postInvalidate();
    246         }
    247 
    248         public long getRemainingTime() {
    249             return (mText.length() - mCurrentCharacter) * TEXT_CHARACTER_DELAY_MS;
    250         }
    251 
    252         public void snapToEnd() {
    253             mCurrentCharacter = mText.length() - 1;
    254         }
    255 
    256         public void setOkArrow(View arrow) {
    257         	mOkArrow = arrow;
    258         }
    259 
    260 
    261 		@Override
    262 		protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    263 			// We need to wait until layout has occurred before we can setup the
    264 			// text page.  Ugh.  Bidirectional dependency!
    265 			if (mParentActivity != null) {
    266 				mParentActivity.processText();
    267 			}
    268 			super.onSizeChanged(w, h, oldw, oldh);
    269 		}
    270 
    271 		@Override
    272         public void onDraw(Canvas canvas) {
    273             final long time = SystemClock.uptimeMillis();
    274             final long delta = time - mLastTime;
    275             if (delta > TEXT_CHARACTER_DELAY_MS) {
    276                 if (mText != null) {
    277                     if (mCurrentCharacter <= mText.length()) {
    278                         CharSequence subtext = mText.subSequence(0, mCurrentCharacter);
    279                         setText(subtext, TextView.BufferType.SPANNABLE);
    280                         mCurrentCharacter++;
    281                         postInvalidateDelayed(TEXT_CHARACTER_DELAY_MS);
    282                     } else {
    283                     	if (mOkArrow != null) {
    284                     		mOkArrow.setVisibility(View.VISIBLE);
    285                     	}
    286                     }
    287                 }
    288             }
    289             super.onDraw(canvas);
    290         }
    291     }
    292 
    293 
    294 
    295 }
    296