EMMA Coverage Report (generated Wed Jun 27 17:43:42 CEST 2012)
[all classes][aarddict.android]

COVERAGE SUMMARY FOR SOURCE FILE [ArticleViewActivity.java]

nameclass, %method, %block, %line, %
ArticleViewActivity.java58%  (11/19)62%  (65/104)55%  (1307/2366)57%  (257,3/455)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ArticleViewActivity$100%   (0/1)0%   (0/2)0%   (0/17)0%   (0/3)
ArticleViewActivity$10 (ArticleViewActivity, Button): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/8)0%   (0/2)
     
class ArticleViewActivity$10$10%   (0/1)0%   (0/2)0%   (0/32)0%   (0/6)
ArticleViewActivity$10$1 (ArticleViewActivity$10): void 0%   (0/1)0%   (0/6)0%   (0/1)
run (): void 0%   (0/1)0%   (0/26)0%   (0/5)
     
class ArticleViewActivity$110%   (0/1)0%   (0/2)0%   (0/37)0%   (0/8)
ArticleViewActivity$11 (ArticleViewActivity, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/28)0%   (0/7)
     
class ArticleViewActivity$120%   (0/1)0%   (0/2)0%   (0/44)0%   (0/8)
ArticleViewActivity$12 (ArticleViewActivity, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/35)0%   (0/7)
     
class ArticleViewActivity$12$10%   (0/1)0%   (0/2)0%   (0/19)0%   (0/5)
ArticleViewActivity$12$1 (ArticleViewActivity$12): void 0%   (0/1)0%   (0/6)0%   (0/1)
onClick (DialogInterface, int): void 0%   (0/1)0%   (0/13)0%   (0/4)
     
class ArticleViewActivity$130%   (0/1)0%   (0/2)0%   (0/70)0%   (0/12)
ArticleViewActivity$13 (ArticleViewActivity, Intent, String): void 0%   (0/1)0%   (0/12)0%   (0/1)
run (): void 0%   (0/1)0%   (0/58)0%   (0/11)
     
class ArticleViewActivity$4$10%   (0/1)0%   (0/2)0%   (0/116)0%   (0/19)
ArticleViewActivity$4$1 (ArticleViewActivity$4, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/107)0%   (0/18)
     
class ArticleViewActivity$8$10%   (0/1)0%   (0/2)0%   (0/34)0%   (0/7)
ArticleViewActivity$8$1 (ArticleViewActivity$8, String): void 0%   (0/1)0%   (0/9)0%   (0/1)
run (): void 0%   (0/1)0%   (0/25)0%   (0/6)
     
class ArticleViewActivity$8100% (1/1)100% (2/2)31%  (54/177)35%  (12/34)
run (): void 100% (1/1)25%  (42/165)33%  (11/33)
ArticleViewActivity$8 (ArticleViewActivity, Entry, HistoryItem): void 100% (1/1)100% (12/12)100% (1/1)
     
class ArticleViewActivity$4100% (1/1)67%  (2/3)34%  (58/173)34%  (11/32)
shouldOverrideUrlLoading (WebView, String): boolean 0%   (0/1)0%   (0/76)0%   (0/13)
onPageFinished (WebView, String): void 100% (1/1)57%  (52/91)56%  (10/18)
ArticleViewActivity$4 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
     
class ArticleViewActivity$1100% (1/1)50%  (1/2)38%  (6/16)25%  (1/4)
onAnimationEnd (Animation): void 0%   (0/1)0%   (0/10)0%   (0/3)
ArticleViewActivity$1 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
     
class ArticleViewActivity$5100% (1/1)50%  (1/2)39%  (9/23)17%  (1/6)
onClick (View): void 0%   (0/1)0%   (0/14)0%   (0/5)
ArticleViewActivity$5 (ArticleViewActivity, Button): void 100% (1/1)100% (9/9)100% (1/1)
     
class ArticleViewActivity$3100% (1/1)67%  (2/3)50%  (26/52)57%  (4/7)
onJsAlert (WebView, String, String, JsResult): boolean 0%   (0/1)0%   (0/26)0%   (0/3)
ArticleViewActivity$3 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
onProgressChanged (WebView, int): void 100% (1/1)100% (20/20)100% (3/3)
     
class ArticleViewActivity$AnimationAdapter100% (1/1)25%  (1/4)50%  (3/6)25%  (1/4)
onAnimationEnd (Animation): void 0%   (0/1)0%   (0/1)0%   (0/1)
onAnimationRepeat (Animation): void 0%   (0/1)0%   (0/1)0%   (0/1)
onAnimationStart (Animation): void 0%   (0/1)0%   (0/1)0%   (0/1)
ArticleViewActivity$AnimationAdapter (): void 100% (1/1)100% (3/3)100% (1/1)
     
class ArticleViewActivity100% (1/1)75%  (48/64)73%  (1057/1456)73%  (219,3/300)
access$1100 (ArticleViewActivity): void 0%   (0/1)0%   (0/3)0%   (0/1)
access$1700 (ArticleViewActivity): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
access$1800 (ArticleViewActivity): AlphaAnimation 0%   (0/1)0%   (0/3)0%   (0/1)
access$1902 (ArticleViewActivity, TimerTask): TimerTask 0%   (0/1)0%   (0/5)0%   (0/1)
access$200 (ArticleViewActivity): TimerTask 0%   (0/1)0%   (0/3)0%   (0/1)
access$2000 (ArticleViewActivity, Entry): void 0%   (0/1)0%   (0/4)0%   (0/1)
access$600 (ArticleViewActivity, HistoryItem): void 0%   (0/1)0%   (0/4)0%   (0/1)
access$700 (ArticleViewActivity, String): void 0%   (0/1)0%   (0/4)0%   (0/1)
access$800 (ArticleViewActivity, String): void 0%   (0/1)0%   (0/4)0%   (0/1)
access$900 (ArticleViewActivity): Timer 0%   (0/1)0%   (0/3)0%   (0/1)
nextArticle (): void 0%   (0/1)0%   (0/17)0%   (0/4)
onKeyLongPress (int, KeyEvent): boolean 0%   (0/1)0%   (0/12)0%   (0/4)
onRestoreInstanceState (Bundle): void 0%   (0/1)0%   (0/25)0%   (0/5)
scrollTo (ScrollXY): void 0%   (0/1)0%   (0/7)0%   (0/2)
showError (String): void 0%   (0/1)0%   (0/8)0%   (0/2)
showMessage (String): void 0%   (0/1)0%   (0/8)0%   (0/2)
goBack (): void 100% (1/1)26%  (16/62)36%  (5/14)
onSearchRequested (): boolean 100% (1/1)30%  (12/40)40%  (3,6/9)
onKeyDown (int, KeyEvent): boolean 100% (1/1)43%  (12/28)56%  (5/9)
onDictionaryServiceReady (): void 100% (1/1)45%  (41/92)48%  (10,6/22)
updateNextButtonVisibility (): void 100% (1/1)49%  (33/68)50%  (9/18)
goToSection (String): void 100% (1/1)54%  (19/35)74%  (3,7/5)
setTitle (HistoryItem): void 100% (1/1)63%  (26/41)86%  (6/7)
showArticle (Entry): void 100% (1/1)70%  (40/57)77%  (10/13)
getScrollPositions (): Map 100% (1/1)77%  (10/13)75%  (3/4)
restoreScrollPos (): boolean 100% (1/1)78%  (25/32)62%  (5/8)
loadAssets (): void 100% (1/1)83%  (30/36)75%  (6/8)
onOptionsItemSelected (MenuItem): boolean 100% (1/1)83%  (20/24)91%  (10/11)
showNext (HistoryItem): void 100% (1/1)84%  (31/37)78%  (7/9)
initUI (): void 100% (1/1)84%  (151/179)82%  (26,9/33)
onPause (): void 100% (1/1)85%  (22/26)88%  (7/8)
viewOnline (): void 100% (1/1)95%  (41/43)99%  (7,9/8)
onPrepareOptionsMenu (Menu): boolean 100% (1/1)97%  (38/39)100% (8/8)
saveScrollPos (int, int): void 100% (1/1)98%  (53/54)92%  (12/13)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
ArticleViewActivity (): void 100% (1/1)100% (9/9)100% (4/4)
access$000 (ArticleViewActivity, int, int): void 100% (1/1)100% (5/5)100% (1/1)
access$100 (): String 100% (1/1)100% (2/2)100% (1/1)
access$1000 (ArticleViewActivity): void 100% (1/1)100% (3/3)100% (1/1)
access$1200 (ArticleViewActivity, HistoryItem): void 100% (1/1)100% (4/4)100% (1/1)
access$1300 (ArticleViewActivity): void 100% (1/1)100% (3/3)100% (1/1)
access$1400 (ArticleViewActivity): void 100% (1/1)100% (3/3)100% (1/1)
access$1500 (ArticleViewActivity, String): String 100% (1/1)100% (4/4)100% (1/1)
access$1600 (ArticleViewActivity): ArticleView 100% (1/1)100% (3/3)100% (1/1)
access$202 (ArticleViewActivity, TimerTask): TimerTask 100% (1/1)100% (5/5)100% (1/1)
access$300 (ArticleViewActivity): List 100% (1/1)100% (3/3)100% (1/1)
access$400 (ArticleViewActivity): boolean 100% (1/1)100% (3/3)100% (1/1)
access$500 (ArticleViewActivity, String): void 100% (1/1)100% (4/4)100% (1/1)
onCreate (Bundle): void 100% (1/1)100% (33/33)100% (7/7)
onCreateOptionsMenu (Menu): boolean 100% (1/1)100% (39/39)100% (5/5)
onDestroy (): void 100% (1/1)100% (15/15)100% (6/6)
onKeyUp (int, KeyEvent): boolean 100% (1/1)100% (10/10)100% (4/4)
onSaveInstanceState (Bundle): void 100% (1/1)100% (28/28)100% (5/5)
readFile (String): String 100% (1/1)100% (39/39)100% (9/9)
resetTitleToCurrent (): void 100% (1/1)100% (18/18)100% (4/4)
scrollTo (int, int): void 100% (1/1)100% (27/27)100% (5/5)
setTitle (CharSequence, CharSequence): void 100% (1/1)100% (16/16)100% (2/2)
showArticle (String, long, String, String): void 100% (1/1)100% (64/64)100% (9/9)
showCurrentArticle (): void 100% (1/1)100% (7/7)100% (2/2)
wrap (String): String 100% (1/1)100% (34/34)100% (1/1)
wrapCSS (String): String 100% (1/1)100% (9/9)100% (1/1)
wrapJS (String): String 100% (1/1)100% (9/9)100% (1/1)
zoomIn (): boolean 100% (1/1)100% (17/17)100% (4/4)
zoomOut (): boolean 100% (1/1)100% (17/17)100% (4/4)
     
class ArticleViewActivity$2100% (1/1)100% (2/2)100% (12/12)100% (3/3)
ArticleViewActivity$2 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
onScroll (int, int, int, int): void 100% (1/1)100% (6/6)100% (2/2)
     
class ArticleViewActivity$6100% (1/1)100% (2/2)100% (11/11)100% (3/3)
ArticleViewActivity$6 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
onTouch (View, MotionEvent): boolean 100% (1/1)100% (5/5)100% (2/2)
     
class ArticleViewActivity$7100% (1/1)100% (2/2)100% (19/19)100% (4/4)
ArticleViewActivity$7 (ArticleViewActivity, HistoryItem): void 100% (1/1)100% (9/9)100% (1/1)
run (): void 100% (1/1)100% (10/10)100% (3/3)
     
class ArticleViewActivity$9100% (1/1)100% (2/2)100% (52/52)100% (7/7)
ArticleViewActivity$9 (ArticleViewActivity): void 100% (1/1)100% (6/6)100% (1/1)
run (): void 100% (1/1)100% (46/46)100% (6/6)

1/* This file is part of Aard Dictionary for Android <http://aarddict.org>.
2 * 
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 3
5 * as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt>
11 * for more details.
12 * 
13 * Copyright (C) 2010 Igor Tkach
14*/
15 
16package aarddict.android;
17 
18import java.io.IOException;
19import java.io.InputStream;
20import java.io.InputStreamReader;
21import java.io.Reader;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.Iterator;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Timer;
30import java.util.TimerTask;
31 
32import aarddict.Article;
33import aarddict.ArticleNotFound;
34import aarddict.Entry;
35import aarddict.LookupWord;
36import aarddict.RedirectTooManyLevels;
37import aarddict.Volume;
38import android.app.AlertDialog;
39import android.app.SearchManager;
40import android.content.DialogInterface;
41import android.content.Intent;
42import android.content.SharedPreferences;
43import android.content.DialogInterface.OnClickListener;
44import android.content.SharedPreferences.Editor;
45import android.net.Uri;
46import android.os.Build;
47import android.os.Bundle;
48import android.util.Log;
49import android.view.KeyEvent;
50import android.view.Menu;
51import android.view.MenuItem;
52import android.view.MotionEvent;
53import android.view.Surface;
54import android.view.View;
55import android.view.Window;
56import android.view.animation.AlphaAnimation;
57import android.view.animation.Animation;
58import android.view.animation.Animation.AnimationListener;
59import android.webkit.JsResult;
60import android.webkit.WebChromeClient;
61import android.webkit.WebView;
62import android.webkit.WebViewClient;
63import android.widget.Button;
64import android.widget.Toast;
65 
66 
67public class ArticleViewActivity extends BaseDictionaryActivity {
68 
69    private final static String TAG = ArticleViewActivity.class.getName();
70 
71    public static final int NOOK_KEY_PREV_LEFT = 92;
72    public static final int NOOK_KEY_NEXT_LEFT = 93;
73 
74    public static final int NOOK_KEY_PREV_RIGHT = 94;
75    public static final int NOOK_KEY_NEXT_RIGHT = 95;
76    
77    private ArticleView         articleView;
78    private String              sharedCSS;
79    private String              mediawikiSharedCSS;
80    private String              mediawikiMonobookCSS;
81    private String              js;
82 
83    private List<HistoryItem>   backItems;
84    private Timer               timer;
85    private TimerTask           currentTask;
86    private TimerTask           currentHideNextButtonTask;
87    private AlphaAnimation                 fadeOutAnimation;
88    private boolean                         useAnimation = false;        
89    
90        private Map<Article, ScrollXY> scrollPositionsH;
91        private Map<Article, ScrollXY> scrollPositionsV;
92        private boolean                saveScrollPos = true;
93    
94    
95    static class AnimationAdapter implements AnimationListener {
96                public void onAnimationEnd(Animation animation) {}
97                public void onAnimationRepeat(Animation animation) {}
98                public void onAnimationStart(Animation animation) {}            
99    }
100    
101    @Override
102    void initUI() {
103            this.scrollPositionsH = Collections.synchronizedMap(new HashMap<Article, ScrollXY>());
104            this.scrollPositionsV = Collections.synchronizedMap(new HashMap<Article, ScrollXY>());
105        loadAssets();                
106 
107        if (DeviceInfo.EINK_SCREEN)        {
108                        useAnimation = false;
109                        N2EpdController.setGL16Mode(2);  // force full screen refresh when changing articles
110 
111                    setContentView(R.layout.eink_article_view);
112                articleView = (ArticleView)findViewById(R.id.EinkArticleView);
113                }
114                // Setup animations only on non-eink screens
115                else
116                {
117                //Animation is broken before 2.1 - animation listener notified,
118                //only sometimes so we can't use it
119                try {
120                        useAnimation = Integer.parseInt(Build.VERSION.SDK) > 6;
121                }        
122                catch (Exception e) {
123                        Log.w(TAG, "Failed to parse SDK version string as int: " + Build.VERSION.SDK);        
124                }
125 
126                fadeOutAnimation = new AlphaAnimation(1f, 0f);
127                fadeOutAnimation.setDuration(600);
128                fadeOutAnimation.setAnimationListener(new AnimationAdapter() {
129                        public void onAnimationEnd(Animation animation) {
130                                Button nextButton = (Button)findViewById(R.id.NextButton);
131                                nextButton.setVisibility(Button.GONE);
132                        }
133                });
134 
135                    getWindow().requestFeature(Window.FEATURE_PROGRESS);
136                    setContentView(R.layout.article_view);
137                    articleView = (ArticleView)findViewById(R.id.ArticleView);    
138                }
139 
140        Log.d(TAG, "Build.VERSION.SDK: " + Build.VERSION.SDK);
141        Log.d(TAG, "use animation? " + useAnimation);
142 
143        timer = new Timer();
144        
145        backItems = Collections.synchronizedList(new LinkedList<HistoryItem>());        
146        
147        articleView.setOnScrollListener(new ArticleView.ScrollListener(){
148                        public void onScroll(int l, int t, int oldl, int oldt) {
149                                saveScrollPos(l, t);
150                        }                
151        });
152        
153            articleView.getSettings().setJavaScriptEnabled(true);
154        
155        articleView.addJavascriptInterface(new SectionMatcher(), "matcher");
156                                
157        articleView.setWebChromeClient(new WebChromeClient(){
158            
159            @Override
160            public boolean onJsAlert(WebView view, String url, String message,
161                            JsResult result) {                    
162                    Log.d(TAG + ".js", String.format("[%s]: %s", url, message));
163                    result.cancel();
164                    return true;
165            }
166            
167            public void onProgressChanged(WebView view, int newProgress) {
168                Log.d(TAG, "Progress: " + newProgress);
169                setProgress(5000 + newProgress * 50);                
170            }
171        });
172                            
173        articleView.setWebViewClient(new WebViewClient() {
174                
175            @Override
176            public void onPageFinished(WebView view, String url) {
177                Log.d(TAG, "Page finished: " + url);
178                currentTask = null;
179                String section = null;
180                                                
181                if (url.contains("#")) {
182                        LookupWord lookupWord = LookupWord.splitWord(url);                    
183                    section = lookupWord.section;
184                    if (backItems.size() > 0) {
185                            HistoryItem currentHistoryItem = backItems.get(backItems.size() - 1); 
186                        HistoryItem h = new HistoryItem(currentHistoryItem);
187                        h.article.section = section;
188                        backItems.add(h);
189                    }
190                }
191                else if (backItems.size() > 0) {
192                    Article current = backItems.get(backItems.size() - 1).article;
193                    section = current.section;
194                }
195                if (!restoreScrollPos()) {
196                        goToSection(section);
197                }
198            }
199            
200            @Override
201            public boolean shouldOverrideUrlLoading(WebView view, final String url) {
202                Log.d(TAG, "URL clicked: " + url);
203                String urlLower = url.toLowerCase(); 
204                if (urlLower.startsWith("http://") ||
205                    urlLower.startsWith("https://") ||
206                    urlLower.startsWith("ftp://") ||
207                    urlLower.startsWith("sftp://") ||
208                    urlLower.startsWith("mailto:")) {
209                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, 
210                                                Uri.parse(url)); 
211                    startActivity(browserIntent);                                         
212                }
213                else {
214                        if (currentTask == null) {
215                                currentTask = new TimerTask() {                                                        
216                                                        public void run() {
217                                                                try {
218                                                                        Article currentArticle = backItems.get(backItems.size() - 1).article;
219                                                                        try {
220                                                                            Iterator<Entry> currentIterator = dictionaryService.followLink(url, currentArticle.volumeId);
221                                            List<Entry> result = new ArrayList<Entry>();
222                                            while (currentIterator.hasNext() && result.size() < 20) {
223                                                result.add(currentIterator.next());
224                                            }                                   
225                                            showNext(new HistoryItem(result));                                                                            
226                                                                        }
227                                                                        catch (ArticleNotFound e) {
228                                                                            showMessage(getString(R.string.msgArticleNotFound, e.word.toString()));
229                                                                        }
230                                                                }                                                                
231                                                                catch (Exception e) {
232                                                                        StringBuilder msgBuilder = new StringBuilder("There was an error following link ")
233                                                                        .append("\"").append(url).append("\"");
234                                                                        if (e.getMessage() != null) {
235                                                                                msgBuilder.append(": ").append(e.getMessage());
236                                                                        }                                                                        
237                                                                        final String msg = msgBuilder.toString(); 
238                                                                        Log.e(TAG, msg, e);
239                                                                        showError(msg);
240                                                                }
241                                                        }
242                                                };
243                                                try {
244                                                    timer.schedule(currentTask, 0);
245                                                }
246                                                catch (Exception e) {
247                                                    Log.d(TAG, "Failed to schedule task", e);
248                                                }
249                        }                        
250                }
251                return true;
252            }
253        });
254        final Button nextButton = (Button)findViewById(R.id.NextButton);
255        nextButton.getBackground().setAlpha(180);
256        nextButton.setOnClickListener(new View.OnClickListener() {                        
257                        public void onClick(View v) {
258                                if (nextButton.getVisibility() == View.VISIBLE) {
259                                        updateNextButtonVisibility();
260                                        nextArticle();
261                                        updateNextButtonVisibility();
262                                }                                                                                        
263                        }
264                });
265                articleView.setOnTouchListener(
266                        new View.OnTouchListener() {                                
267                                public boolean onTouch(View v, MotionEvent event) {
268                                        updateNextButtonVisibility();
269                                        return false;
270                                }
271                        }
272                );
273        setProgressBarVisibility(true);
274    }
275 
276    private void scrollTo(ScrollXY s) {
277            scrollTo(s.x, s.y);
278    }
279    
280    private void scrollTo(int x, int y) {
281            saveScrollPos = false; 
282            Log.d(TAG, "Scroll to " + x + ", " + y);
283            articleView.scrollTo(x, y);
284            saveScrollPos = true;
285    }
286    
287    private void goToSection(String section) {
288            Log.d(TAG, "Go to section " + section);
289            if (section == null || section.trim().equals("")) {
290                    scrollTo(0, 0);
291            }
292            else {
293                    articleView.loadUrl(String.format("javascript:scrollToMatch(\"%s\")", section));
294            }
295    }    
296    
297    @Override
298    public boolean onKeyDown(int keyCode, KeyEvent event) {
299        switch (keyCode) {
300            case KeyEvent.KEYCODE_BACK:
301                goBack();   
302                break;
303                    case NOOK_KEY_PREV_LEFT:
304                    case NOOK_KEY_PREV_RIGHT:
305            case KeyEvent.KEYCODE_VOLUME_UP:
306                if (!articleView.pageUp(false)) {
307                    goBack();
308                }
309                break;
310            case KeyEvent.KEYCODE_VOLUME_DOWN:
311                    case NOOK_KEY_NEXT_LEFT:
312                    case NOOK_KEY_NEXT_RIGHT:
313                if (!articleView.pageDown(false)) {
314                    nextArticle();
315                };
316                break;
317            default:
318                return super.onKeyDown(keyCode, event);
319        }
320        return true;
321    }
322    
323    @Override
324    public boolean onKeyUp(int keyCode, KeyEvent event) {
325            //eat key ups corresponding to key downs so that volume keys don't beep
326    switch (keyCode) {
327        case KeyEvent.KEYCODE_BACK:
328        case KeyEvent.KEYCODE_VOLUME_UP:
329        case KeyEvent.KEYCODE_VOLUME_DOWN:
330            break;
331        default:
332            return super.onKeyDown(keyCode, event);
333        }
334        return true;
335    }
336        
337    private boolean zoomIn() {        
338        boolean zoomed = articleView.zoomIn();
339        float scale = articleView.getScale();
340        articleView.setInitialScale(Math.round(scale*100));
341        return zoomed;
342    }
343    
344    private boolean zoomOut() {
345        boolean zoomed = articleView.zoomOut();
346        float scale = articleView.getScale();
347        articleView.setInitialScale(Math.round(scale*100));
348        return zoomed;            
349    }
350        
351    private void goBack() {
352        if (backItems.size() == 1) {
353            finish();
354        }        
355            if (currentTask != null) {
356                    return;
357            }
358        if (backItems.size() > 1) {
359            HistoryItem current = backItems.remove(backItems.size() - 1); 
360            HistoryItem prev = backItems.get(backItems.size() - 1);
361            
362            Article prevArticle = prev.article; 
363            if (prevArticle.equalsIgnoreSection(current.article)) {
364                    resetTitleToCurrent();
365                    if (!prevArticle.sectionEquals(current.article) && !restoreScrollPos()) {
366                            goToSection(prevArticle.section);
367                    }
368            }   
369            else {
370                    showCurrentArticle();
371            }
372        }
373    }
374            
375    private void nextArticle() {
376            HistoryItem current = backItems.get(backItems.size() - 1);
377            if (current.hasNext()) {
378                    showNext(current);
379            }
380    }
381 
382    @Override
383    public boolean onSearchRequested() {
384        Intent intent = getIntent();
385        if (intent != null && intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEARCH)) {
386                    Intent next = new Intent();
387                next.setClass(this, LookupActivity.class);
388                    next.setAction(Intent.ACTION_SEARCH);
389                      next.putExtra(SearchManager.QUERY, intent.getStringExtra("query"));
390                      startActivity(next);
391        }
392        finish();
393        return true;
394    }
395    
396    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
397            if (keyCode == KeyEvent.KEYCODE_SEARCH){
398                    finish();
399                    return true;
400            }
401            return super.onKeyLongPress(keyCode, event);
402    }
403 
404    
405    final static int MENU_VIEW_ONLINE = 1;
406    final static int MENU_NEW_LOOKUP = 2;
407    final static int MENU_ZOOM_IN = 3;
408    final static int MENU_ZOOM_OUT = 4;
409    
410    private MenuItem miViewOnline; 
411    
412    @Override
413    public boolean onCreateOptionsMenu(Menu menu) {
414        miViewOnline = menu.add(0, MENU_VIEW_ONLINE, 0, R.string.mnViewOnline).setIcon(android.R.drawable.ic_menu_view);
415        menu.add(0, MENU_NEW_LOOKUP, 0, R.string.mnNewLookup).setIcon(android.R.drawable.ic_menu_search);        
416        menu.add(0, MENU_ZOOM_OUT, 0, R.string.mnZoomOut).setIcon(R.drawable.ic_menu_zoom_out);
417        menu.add(0, MENU_ZOOM_IN, 0, R.string.mnZoomIn).setIcon(R.drawable.ic_menu_zoom_in);
418        return true;
419    }
420    
421    @Override
422    public boolean onPrepareOptionsMenu(Menu menu) {
423            boolean enableViewOnline = false;
424        if (this.backItems.size() > 0) {
425            HistoryItem historyItem = backItems.get(backItems.size() - 1);
426            Article current = historyItem.article;
427            Volume d = dictionaryService.getVolume(current.volumeId);
428            enableViewOnline = d.getArticleURLTemplate() != null;            
429        }                
430            miViewOnline.setEnabled(enableViewOnline);
431            return true;
432    }
433    
434    @Override
435    public boolean onOptionsItemSelected(MenuItem item) {
436        switch (item.getItemId()) {
437        case MENU_VIEW_ONLINE:
438            viewOnline();
439            break;
440        case MENU_NEW_LOOKUP:
441            onSearchRequested();
442            break;
443        case MENU_ZOOM_IN:
444            zoomIn();
445            break;
446        case MENU_ZOOM_OUT:
447            zoomOut();
448            break;
449        default:
450            return super.onOptionsItemSelected(item);
451        }
452        return true;
453    }
454        
455    private void viewOnline() {
456        if (this.backItems.size() > 0) {            
457            Article current = this.backItems.get(this.backItems.size() - 1).article;
458            Volume d = dictionaryService.getVolume(current.volumeId);
459            String url = d == null ? null : d.getArticleURL(current.title);
460            if (url != null) {
461                Intent browserIntent = new Intent(Intent.ACTION_VIEW, 
462                        Uri.parse(url)); 
463                startActivity(browserIntent);                                         
464            }
465        }
466    }
467    
468    private void showArticle(String volumeId, long articlePointer, String word, String section) {
469        Log.d(TAG, "word: " + word);
470        Log.d(TAG, "dictionaryId: " + volumeId);
471        Log.d(TAG, "articlePointer: " + articlePointer);
472        Log.d(TAG, "section: " + section);                       
473        Volume d = dictionaryService.getVolume(volumeId);        
474        Entry entry = new Entry(d.getId(), word, articlePointer);
475        entry.section = section;        
476        this.showArticle(entry);
477    }
478    
479    private void showArticle(Entry entry) {        
480        List<Entry> result = new ArrayList<Entry>();
481        result.add(entry);
482        
483        try {
484            Iterator<Entry> currentIterator = dictionaryService.followLink(entry.title, entry.volumeId);            
485            while (currentIterator.hasNext() && result.size() < 20) {
486                Entry next = currentIterator.next();
487                if (!next.equals(entry)) {
488                    result.add(next);
489                }
490            }                                                                                    
491        }
492        catch (ArticleNotFound e) {
493            Log.d(TAG, String.format("Article \"%s\" not found - unexpected", e.word));
494        }        
495        showNext(new HistoryItem(result));
496    }    
497 
498    private Map<Article, ScrollXY> getScrollPositions() {
499                int orientation = getWindowManager().getDefaultDisplay().getOrientation();
500                switch (orientation) {
501                        case Surface.ROTATION_0:
502                        case Surface.ROTATION_180:
503                                return scrollPositionsV;
504                        default:
505                                return scrollPositionsH;
506                }
507    }
508    
509    private void saveScrollPos(int x, int y) {
510            if (!saveScrollPos) {
511                    //Log.d(TAG, "Not saving scroll position (disabled)");
512                    return;
513            }
514            if (backItems.size() > 0) {
515                    Article a = backItems.get(backItems.size() - 1).article;
516                    Map<Article, ScrollXY> positions = getScrollPositions();
517                    ScrollXY s = positions.get(a);
518                    if (s == null) {
519                            s = new ScrollXY(x, y);
520                            positions.put(a, s);
521                    }
522                    else {
523                            s.x = x;
524                            s.y = y;
525                    }
526                    //Log.d(TAG, String.format("Saving scroll position %s for %s", s, a.title));
527                    getScrollPositions().put(a, s);            
528            }
529    }
530    
531    private boolean restoreScrollPos() {
532            if (backItems.size() > 0) { 
533                    Article a = backItems.get(backItems.size() - 1).article;            
534                    ScrollXY s = getScrollPositions().get(a);
535                    if (s == null) {
536                            return false;
537                    }
538                    scrollTo(s);
539                    return true;
540            }
541            return false;
542    }
543    
544    private void showNext(HistoryItem item_) {
545            final HistoryItem item = new HistoryItem(item_);
546            final Entry entry = item.next();
547            runOnUiThread(new Runnable() {
548                        public void run() {
549                                setTitle(item);
550                                setProgress(1000);
551                        }
552                });            
553            currentTask = new TimerTask() {
554                        public void run() {
555                        try {
556                                Article a = dictionaryService.getArticle(entry);                                                                
557                                try {
558                                    a = dictionaryService.redirect(a);
559                                    item.article = new Article(a);
560                                }            
561                                catch (ArticleNotFound e) {
562                                    showMessage(getString(R.string.msgRedirectNotFound, e.word.toString()));
563                                    return;
564                                }
565                                catch (RedirectTooManyLevels e) {
566                                    showMessage(getString(R.string.msgTooManyRedirects, a.getRedirect()));
567                                    return;
568                                }
569                                catch (Exception e) {
570                                        Log.e(TAG, "Redirect failed", e);
571                                    showError(getString(R.string.msgErrorLoadingArticle, a.title));
572                                    return;
573                                }
574                                
575                                HistoryItem oldCurrent = null;
576                                if (!backItems.isEmpty())
577                                        oldCurrent = backItems.get(backItems.size() - 1);
578                                
579                                backItems.add(item);
580                                
581                                if (oldCurrent != null) {
582                                        HistoryItem newCurrent = item;
583                                    if (newCurrent.article.equalsIgnoreSection(oldCurrent.article)) {
584                                        
585                                            final String section = oldCurrent.article.sectionEquals(newCurrent.article) ? null : newCurrent.article.section;
586                                            
587                                            runOnUiThread(new Runnable() {                                                                
588                                                                public void run() {
589                                                                        resetTitleToCurrent();
590                                                                        if (section != null) {
591                                                                            goToSection(section);
592                                                                        }
593                                                                        setProgress(10000);
594                                                                        currentTask = null;
595                                                                }
596                                                        });                                        
597                                    }   
598                                    else {
599                                            showCurrentArticle();
600                                    }                                        
601                                }
602                                else {
603                                        showCurrentArticle();
604                                }                                                                                                                        
605                        }
606                        catch (Exception e) {
607                            String msg = getString(R.string.msgErrorLoadingArticle, entry.title);
608                                Log.e(TAG, msg, e);
609                                showError(msg);
610                        }
611                        }
612            };
613            try {
614                timer.schedule(currentTask, 0);
615            }
616            catch (Exception e) {
617                Log.d(TAG, "Failed to schedule task", e);
618            }
619    }
620        
621    private void showCurrentArticle() {
622            runOnUiThread(new Runnable() {                        
623                        public void run() {                        
624                        setProgress(5000);
625                        resetTitleToCurrent();                       
626                        Article a = backItems.get(backItems.size() - 1).article;
627                        Log.d(TAG, "Show article: " + a.text);        
628                        articleView.loadDataWithBaseURL("", wrap(a.text), "text/html", "utf-8", null);
629                        }
630                });
631    }
632        
633    private void updateNextButtonVisibility() {
634            if (currentHideNextButtonTask != null) {
635                    currentHideNextButtonTask.cancel();
636                    currentHideNextButtonTask = null;
637            }
638            boolean hasNextArticle = false;
639        if (backItems.size() > 0) {
640            HistoryItem historyItem = backItems.get(backItems.size() - 1);
641            hasNextArticle = historyItem.hasNext();
642        }
643        final Button nextButton = (Button)findViewById(R.id.NextButton);
644        if (hasNextArticle) {
645                if (nextButton.getVisibility() == View.GONE){
646                        nextButton.setVisibility(View.VISIBLE);
647                }                
648                currentHideNextButtonTask = new TimerTask() {                        
649                        @Override
650                        public void run() {
651                                runOnUiThread(new Runnable() {                                        
652                                        public void run() {
653                                        if (useAnimation) {
654                                                nextButton.startAnimation(fadeOutAnimation);
655                                        }
656                                        else {
657                                                nextButton.setVisibility(View.GONE);
658                                        }
659                                                currentHideNextButtonTask = null;
660                                        }
661                                });                        
662                        }
663                }; 
664                try {
665                        timer.schedule(currentHideNextButtonTask, 1800);                        
666                }
667                catch (IllegalStateException e) {
668                    //this may happen if orientation changes while users touches screen                       
669                    Log.d(TAG, "Failed to schedule \"Next\" button hide", e);                        
670                }
671        }        
672        else {
673                nextButton.setVisibility(View.GONE);
674        }
675    }
676        
677    private void showMessage(final String message) {
678            runOnUiThread(new Runnable() {
679                        public void run() {
680                            currentTask = null;
681                            setProgress(10000);
682                            resetTitleToCurrent();
683                        Toast.makeText(ArticleViewActivity.this, message, Toast.LENGTH_LONG).show();
684                        if (backItems.isEmpty()) {
685                            finish();
686                        }                                        
687                        }
688                });
689    }
690 
691    private void showError(final String message) {
692            runOnUiThread(new Runnable() {
693                        public void run() {
694                            currentTask = null;
695                            setProgress(10000);
696                            resetTitleToCurrent();
697                        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ArticleViewActivity.this);
698                        dialogBuilder.setTitle(R.string.titleError).setMessage(message).setNeutralButton(R.string.btnDismiss, new OnClickListener() {            
699                            public void onClick(DialogInterface dialog, int which) {
700                                dialog.dismiss();
701                                if (backItems.isEmpty()) {
702                                    finish();
703                                }
704                            }
705                        });
706                        dialogBuilder.show();
707                        }
708                });            
709    }
710    
711        
712    private void setTitle(CharSequence articleTitle, CharSequence dictTitle) {
713            setTitle(getString(R.string.titleArticleViewActivity, articleTitle, dictTitle));
714    }        
715    
716    private void resetTitleToCurrent() {                                
717            if (!backItems.isEmpty()) {
718                    HistoryItem current = backItems.get(backItems.size() - 1);
719                    setTitle(current);
720            }
721    }
722    
723    private void setTitle(HistoryItem item) {                
724                StringBuilder title = new StringBuilder();
725                if (item.entries.size() > 1) {
726                        title
727                        .append(item.entryIndex + 1)
728                        .append("/")
729                        .append(item.entries.size())
730                        .append(" ");
731                }
732                Entry entry = item.current();
733                title.append(entry.title);
734                setTitle(title, dictionaryService.getDisplayTitle(entry.volumeId));            
735    }
736    
737    private String wrap(String articleText) {
738        return new StringBuilder("<html>")
739        .append("<head>")
740        .append(this.sharedCSS)
741        .append(this.mediawikiSharedCSS)
742        .append(this.mediawikiMonobookCSS)
743        .append(this.js)
744        .append("</head>")
745        .append("<body>")
746        .append("<div id=\"globalWrapper\">")        
747        .append(articleText)
748        .append("</div>")
749        .append("</body>")
750        .append("</html>")
751        .toString();
752    }
753    
754    private String wrapCSS(String css) {
755        return String.format("<style type=\"text/css\">%s</style>", css);
756    }
757 
758    private String wrapJS(String js) {
759        return String.format("<script type=\"text/javascript\">%s</script>", js);
760    }
761    
762    private void loadAssets() {
763        try {
764            this.sharedCSS = wrapCSS(readFile("shared.css"));
765            this.mediawikiSharedCSS = wrapCSS(readFile("mediawiki_shared.css"));
766            this.mediawikiMonobookCSS = wrapCSS(readFile("mediawiki_monobook.css"));
767            this.js = wrapJS(readFile("aar.js"));
768        }
769        catch (IOException e) {
770            Log.e(TAG, "Failed to load assets", e);
771        }        
772    }
773    
774    private String readFile(String name) throws IOException {
775        final char[] buffer = new char[0x1000];
776        StringBuilder out = new StringBuilder();
777        InputStream is = getResources().getAssets().open(name);
778        Reader in = new InputStreamReader(is, "UTF-8");
779        int read;
780        do {
781          read = in.read(buffer, 0, buffer.length);
782          if (read>0) {
783            out.append(buffer, 0, read);
784          }
785        } while (read>=0);
786        return out.toString();
787    }
788 
789    
790    @Override
791    protected void onPause() {
792            super.onPause();
793            SharedPreferences prefs = getPreferences(MODE_PRIVATE);
794            Editor e = prefs.edit();
795            e.putFloat("articleView.scale", articleView.getScale());
796            boolean success = e.commit();
797            if (!success) {
798                    Log.w(TAG, "Failed to save article view scale pref");
799            }
800    }
801    
802    @Override
803    protected void onCreate(Bundle savedInstanceState) {
804            super.onCreate(savedInstanceState);
805            SharedPreferences prefs = getPreferences(MODE_PRIVATE);            
806            float scale = prefs.getFloat("articleView.scale", 1.0f);
807            int initialScale = Math.round(scale*100);
808            Log.d(TAG, "Setting initial article view scale to " + initialScale);
809            articleView.setInitialScale(initialScale);
810    }
811    
812    @Override
813    protected void onDestroy() {
814            super.onDestroy();
815            timer.cancel();
816            scrollPositionsH.clear();
817            scrollPositionsV.clear();
818            backItems.clear();
819    }
820 
821    @Override
822    void onDictionaryServiceReady() {
823            if (this.backItems.isEmpty()) {
824                final Intent intent = getIntent();                
825                if (intent != null && intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEARCH)) {
826                    final String word = intent.getStringExtra("query");
827                    
828                    if (currentTask != null) {
829                        currentTask.cancel();                        
830                    }
831                    
832                    currentTask = new TimerTask() {                    
833                    @Override
834                    public void run() {
835                        setProgress(500);                                      
836                        Log.d(TAG, "intent.getDataString(): " + intent.getDataString());
837                        Iterator<Entry> results = dictionaryService.lookup(word);
838                        Log.d(TAG, "Looked up " + word );
839                        if (results.hasNext()) {
840                            currentTask = null;
841                            Entry entry = results.next();
842                            showArticle(entry);
843                        }
844                        else {
845                                onSearchRequested();
846                        }
847                    }
848                };
849                    
850                try {
851                    timer.schedule(currentTask, 0);
852                }
853                catch (Exception e) {
854                    Log.d(TAG, "Failed to schedule task", e);
855                    showError(getString(R.string.msgErrorLoadingArticle, word));
856                }                    
857                }
858                else {
859                    String word = intent.getStringExtra("word");                
860                    String section = intent.getStringExtra("section");                
861                    String volumeId = intent.getStringExtra("volumeId");
862                    long articlePointer = intent.getLongExtra("articlePointer", -1);
863                    dictionaryService.setPreferred(volumeId);
864                    showArticle(volumeId, articlePointer, word, section);
865                }
866            }
867            else {
868                    showCurrentArticle();                    
869            }
870    }    
871    
872    @SuppressWarnings("unchecked")
873    @Override
874    protected void onSaveInstanceState(Bundle outState) {
875            super.onSaveInstanceState(outState);
876            outState.putSerializable("backItems", new LinkedList(backItems));
877            outState.putSerializable("scrollPositionsH", new HashMap(scrollPositionsH));
878            outState.putSerializable("scrollPositionsV", new HashMap(scrollPositionsV));            
879    }
880    
881    @SuppressWarnings("unchecked")
882        @Override
883    protected void onRestoreInstanceState(Bundle savedInstanceState) {
884            super.onRestoreInstanceState(savedInstanceState);
885            backItems = Collections.synchronizedList((List)savedInstanceState.getSerializable("backItems"));
886            scrollPositionsH = Collections.synchronizedMap((Map)savedInstanceState.getSerializable("scrollPositionsH"));
887            scrollPositionsV = Collections.synchronizedMap((Map)savedInstanceState.getSerializable("scrollPositionsV"));
888    }        
889}

[all classes][aarddict.android]
EMMA 0.0.0 (unsupported private build) (C) Vladimir Roubtsov