• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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 
formatPages(Conversation conversation, TextView textView)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
onTouchEvent(MotionEvent event)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 
showPage(ConversationUtils.ConversationPage page)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 
processText()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 
TypewriterTextView(Context context)225         public TypewriterTextView(Context context) {
226             super(context);
227         }
228 
TypewriterTextView(Context context, AttributeSet attrs)229         public TypewriterTextView(Context context, AttributeSet attrs) {
230             super(context, attrs);
231         }
232 
TypewriterTextView(Context context, AttributeSet attrs, int defStyle)233         public TypewriterTextView(Context context, AttributeSet attrs, int defStyle) {
234             super(context, attrs, defStyle);
235         }
236 
setParentActivity(ConversationDialogActivity parent)237         public void setParentActivity(ConversationDialogActivity parent) {
238         	mParentActivity = parent;
239         }
240 
setTypewriterText(CharSequence text)241         public void setTypewriterText(CharSequence text) {
242             mText = text;
243             mCurrentCharacter = 0;
244             mLastTime = 0;
245             postInvalidate();
246         }
247 
getRemainingTime()248         public long getRemainingTime() {
249             return (mText.length() - mCurrentCharacter) * TEXT_CHARACTER_DELAY_MS;
250         }
251 
snapToEnd()252         public void snapToEnd() {
253             mCurrentCharacter = mText.length() - 1;
254         }
255 
setOkArrow(View arrow)256         public void setOkArrow(View arrow) {
257         	mOkArrow = arrow;
258         }
259 
260 
261 		@Override
onSizeChanged(int w, int h, int oldw, int oldh)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
onDraw(Canvas canvas)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