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
17package com.replica.replicaisland;
18
19import java.util.ArrayList;
20
21import android.app.Activity;
22import android.content.Context;
23import android.content.Intent;
24import android.graphics.Canvas;
25import android.graphics.Paint;
26import android.graphics.drawable.AnimationDrawable;
27import android.os.Bundle;
28import android.os.SystemClock;
29import android.text.SpannableStringBuilder;
30import android.text.TextUtils;
31import android.util.AttributeSet;
32import android.view.MotionEvent;
33import android.view.View;
34import android.widget.ImageView;
35import android.widget.TextView;
36
37import com.replica.replicaisland.ConversationUtils.Conversation;
38import com.replica.replicaisland.ConversationUtils.ConversationPage;
39
40public 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