Register Guidelines E-Books Search Today's Posts Mark Forums Read

Go Back   MobileRead Forums > E-Book Readers > Sony Reader > Sony Reader Dev Corner

Notices

Reply
 
Thread Tools Search this Thread
Old 01-14-2012, 10:54 AM   #1
robyshot
Enthusiast
robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'robyshot knows the difference between 'who' and 'whom'
 
robyshot's Avatar
 
Posts: 32
Karma: 10040
Join Date: Jan 2012
Device: Sony Prs T1
No refresh mode in every app

Hello

i was using the sony browser on my t1 and i left it alone for something like 5/10 minutes cause i had to do some stuff,when i picked it back something odd happened: the screen did not refreshed, like when you scroll a webpage and the screen enter a low definition mode to avoid continuous flashing due to e-ink refresh; i tried using some app (adw launcher,rssdemon,dolphin browser mini etc.) and saw it was applied globally!

Maybe this non refresh mode can be activated outside the stock browser for the other apps,i think it would be really useful

i made a video (and discovered that returning to the home deactivated the trick )

http://www.youtube.com/watch?v=o1PslEW6uLE


I'll try to reproduce this behavior and add further details

Last edited by robyshot; 01-14-2012 at 10:56 AM.
robyshot is offline   Reply With Quote
Old 01-14-2012, 11:44 AM   #2
rubentje1991
Enthusiast
rubentje1991 began at the beginning.
 
Posts: 33
Karma: 10
Join Date: Jan 2012
Device: Sony PRS-T1
Wow, that would be very nice (in my calendar app for example)!

Eagerly awaiting your testing
rubentje1991 is offline   Reply With Quote
Old 01-14-2012, 03:21 PM   #3
The-eBook-Reader
Groupie
The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.The-eBook-Reader is fluent in JavaScript as well as Klingon.
 
The-eBook-Reader's Avatar
 
Posts: 194
Karma: 4637
Join Date: Sep 2009
Location: United States
Device: All
Nice find! I figured out how to replicate this. When scrolling with the default web browser there's a delay before it resets. Just scroll with the browser and then quickly tap the top of the screen to open the notifications bar. It will keep it from resetting and enter it in a semi-permanent partial refresh state. Going to the Sony homescreen or opening an ebook with the Sony Reader app resets it back to normal.

This partial refresh trick is actually really helpful for whenever you need to scroll through lists like Dropbox and the file manager.

It also works with FBReader for perma-partial page refresh. It doesn't look too bad but the text is definitely rougher than usual. It makes page turns faster, especially when holding down.

Last edited by The-eBook-Reader; 01-14-2012 at 03:33 PM.
The-eBook-Reader is offline   Reply With Quote
Old 01-16-2012, 07:52 AM   #4
vortigern
Member
vortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enough
 
Posts: 13
Karma: 662
Join Date: Jan 2012
Device: PRS-T1
Funky find!
... Much more usable like that but does get too lumpy in some apps - Just need to find a way to force a refresh without taking it out of scroll mode
vortigern is offline   Reply With Quote
Old 01-16-2012, 01:07 PM   #5
uboot
Evangelist
uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.
 
Posts: 425
Karma: 75216
Join Date: Nov 2011
Location: old europe
Device: Kobo Mini, Tolino Epos 2
I also ran into this several times, but it turned everything to 1-bit black&white, i.e. no grey scale anymore....

But as far as I remember there are driver settings (either at /sys or /proc) that determine at which ratio of changed screen content there will be a full refresh. And these settings should be tweakable! Just need to find the time to do more research....
uboot is offline   Reply With Quote
Old 01-16-2012, 04:46 PM   #6
rubentje1991
Enthusiast
rubentje1991 began at the beginning.
 
Posts: 33
Karma: 10
Join Date: Jan 2012
Device: Sony PRS-T1
Wink

Indeed, that works perfectly... you've definitely less pixels (and details), but that's logically...

If someone has some time to dig in deeper, maybe it would be possible to activate some "switch"-script by pressing the home or menu button for e.g. 2 seconds (which then activates/deactivates the refresh mode)
=> but we can already use it sometimes (when you know you're going to scroll to much, and when there is some wifi-connection :-) )
rubentje1991 is offline   Reply With Quote
Old 01-16-2012, 05:05 PM   #7
fjtorres
Grand Sorcerer
fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.fjtorres ought to be getting tired of karma fortunes by now.
 
Posts: 11,732
Karma: 128354696
Join Date: May 2009
Location: 26 kly from Sgr A*
Device: T100TA,PW2,PRS-T1,KT,FireHD 8.9,K2, PB360,BeBook One,Axim51v,TC1000
Quote:
Originally Posted by rubentje1991 View Post
Indeed, that works perfectly... you've definitely less pixels (and details), but that's logically...

If someone has some time to dig in deeper, maybe it would be possible to activate some "switch"-script by pressing the home or menu button for e.g. 2 seconds (which then activates/deactivates the refresh mode)
=> but we can already use it sometimes (when you know you're going to scroll to much, and when there is some wifi-connection :-) )
You don't need an active Wi-Fi connection on a Rooted T1; you can launch the Browser from Zeam without one, pinch, and click the bar in an instant.
I got it to work on my very first try.

The setting survives sleep mode, BTW, and switching to other non-Sony apps. And yes, it looks like it switches to a pure B&W mode; Coolreader and Kindle work fine. Aldiko, not so much.
But paging in all three apps is ridiculously fast--no black flash at all, for those that care--so if a 2-bit mode could be enabled via a software switch, it should still be a major speed boost with no significant text quality loss.
fjtorres is offline   Reply With Quote
Old 01-17-2012, 07:29 AM   #8
vortigern
Member
vortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enoughvortigern will become famous soon enough
 
Posts: 13
Karma: 662
Join Date: Jan 2012
Device: PRS-T1
I'm thinking there should be a way of attaching a method to change modes to the global TouchDown/Up methods (if they exist!) and that there should be some clue in the browser code.

So I've extracted the browser code and found this file Here which has a lot of hopeful variables in it like INVALIDATE_DELAY_FOR_CHANGE_EINK_MODE and methods like startTouch etc, although as I've not done any android dev or whatever this ddx is before it's all a bit foreign at the moment.

I'll let you know if I find anything useful!
vortigern is offline   Reply With Quote
Old 01-18-2012, 12:23 PM   #9
rupor
meat popsicle
rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!rupor rocks like Gibraltar!
 
rupor's Avatar
 
Posts: 225
Karma: 100000
Join Date: Jul 2007
Location: USA
Device: Kindles, Pixels, iPads
Quote:
Originally Posted by vortigern View Post
I'm thinking there should be a way of attaching a method to change modes to the global TouchDown/Up methods (if they exist!) and that there should be some clue in the browser code.
It looks rather simple, actually - unless I am mistaken.

Standard Android SDK has been modified and new interface introduced: ViewParentEink. It has following methods (which track standard View methods with an extra parameter at the end - updateMode):

.method public final invalidateChild(Landroid/view/View;Landroid/graphics/Rect;I)V
.registers 20
.parameter "child"
.parameter "dirty"
.parameter "updateMode"


.method public invalidateChildInParent([ILandroid/graphics/Rect;I)Landroid/view/ViewParentEink;
.registers 11
.parameter "location"
.parameter "dirty"
.parameter "updateMode"

Interface has been implemented in ViewGroup - so any view which implements AbsoluteLayout has access to those methods.

Sony's browser WebView keeps current updateMode in a class variable and updates it when necessary (touch, zoom with eInk mode constants). All calls to invalidate are using this "new" framework functions - the rest happens automatically.

Something like that...
rupor is offline   Reply With Quote
Old 01-18-2012, 12:59 PM   #10
uboot
Evangelist
uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.uboot seems famous, but is in fact legendary.
 
Posts: 425
Karma: 75216
Join Date: Nov 2011
Location: old europe
Device: Kobo Mini, Tolino Epos 2
According to dmesg, the T1 has a TPS6518x eInk powermanagement IC (although with Freescale i.MX508 instead of OMAP):

http://focus.ti.com/pdfs/wtbu/SWPT045.pdf

Does anybody know, what the Nook is based on?

Maybe drivers are interchangeable....
uboot is offline   Reply With Quote
Old 01-19-2012, 07:19 PM   #11
Morkl
Connoisseur
Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.
 
Posts: 80
Karma: 68347
Join Date: Oct 2009
Location: Sweden
Device: PRS-T1
Quote:
Originally Posted by rupor View Post
It looks rather simple, actually - unless I am mistaken.

Standard Android SDK has been modified and new interface introduced: ViewParentEink. It has following methods (which track standard View methods with an extra parameter at the end - updateMode):

.method public final invalidateChild(Landroid/view/View;Landroid/graphics/Rect;I)V
.registers 20
.parameter "child"
.parameter "dirty"
.parameter "updateMode"


.method public invalidateChildInParent([ILandroid/graphics/Rect;I)Landroid/view/ViewParentEink;
.registers 11
.parameter "location"
.parameter "dirty"
.parameter "updateMode"

Interface has been implemented in ViewGroup - so any view which implements AbsoluteLayout has access to those methods.

Sony's browser WebView keeps current updateMode in a class variable and updates it when necessary (touch, zoom with eInk mode constants). All calls to invalidate are using this "new" framework functions - the rest happens automatically.

Something like that...
Just checking in to confirm this. My experiments show that update mode 4 is flashy, 5 is b/w and fast (and I guess not as easy to get out of as the other ones?) and 2 or 3 may be suitable for "normal" use.

I made an "EinkListView" class when experimenting, which is just a ListView that overrides all the methods that have updateMode overloads in Sony's android.view.View to use the updateMode ones:

Code:
public class EinkListView extends ListView {
	public int mUpdateMode = 0;
	
	public EinkListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public EinkListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public EinkListView(Context context) {
		super(context);
	}

	@Override
	public void invalidate() {
		super.invalidate(mUpdateMode);
	}

	@Override
	public void invalidate(int l, int t, int r, int b) {
		super.invalidate(l, t, r, b, mUpdateMode);
	}

	@Override
	public void invalidate(Rect dirty) {
		super.invalidate(dirty, mUpdateMode);
	}

	@Override
	public void invalidateDrawable(Drawable drawable) {
		super.invalidateDrawable(drawable, mUpdateMode);
	}

	@Override
	public void postInvalidate() {
		super.postInvalidate(mUpdateMode);
	}

	@Override
	public void postInvalidate(int left, int top, int right, int bottom) {
		super.postInvalidate(left, top, right, bottom, mUpdateMode);
	}

	@Override
	public void postInvalidateDelayed(long delayMilliseconds, int left,
			int top, int right, int bottom) {
		super.postInvalidateDelayed(delayMilliseconds, left, top, right, bottom, mUpdateMode);
	}

	@Override
	public void postInvalidateDelayed(long delayMilliseconds) {
		super.postInvalidateDelayed(delayMilliseconds, mUpdateMode);
	}

	@Override
	public void scrollBy(int x, int y) {
		super.scrollBy(x, y, mUpdateMode);
	}

	@Override
	public void scrollTo(int x, int y) {
		super.scrollTo(x, y, mUpdateMode);
	}
	
}
N.B.: I had to insert Sony's android.view.View into the android.jar in my android-sdk/platforms/android-8 dir to get the IDE to accept the calls to methods that don't usually exist.
Morkl is offline   Reply With Quote
Old 01-20-2012, 02:20 AM   #12
bardo
Member
bardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enough
 
Posts: 17
Karma: 548
Join Date: Jan 2012
Device: PRS-T1
Quote:
Originally Posted by Morkl View Post
I made an "EinkListView" class when experimenting, which is just a ListView that overrides all the methods that have updateMode overloads in Sony's android.view.View to use the updateMode ones:
In order to see more e-ink friendly applications and to get app designers on board, we need a replacement class for the usual WebView.

Being not an Android programmer, I looked around for generic code which could make it in such a replacement. So far I found these:

- The flicker-free page turn code used in cool reader:
credits to DairyKnight <dairyknight@gmail.com>
http://crengine.git.sourceforge.net/...ontroller.java

- Max Kammerer's code for his nook port of aarddict:
http:///github.com/max-kammerer/aarddict_nook
Code:
// from kbs - trook.projectsource code.

private final void pageUp() {
        int cury = articleView2.getScrollY();
        if (cury == 0) { return; }
        int newy = cury - WEB_SCROLL_PX;
        if (newy < 0) {
            newy = 0;
        }
        articleView2.scrollTo(0, newy);

}

private final void pageDown() {

        int cury = articleView2.getScrollY();
        int hmax = articleView2.getContentHeight() - 200;
        if (hmax < 0) {
            hmax = 0;
        }
        int newy = cury + WEB_SCROLL_PX;
        if (newy > hmax) {
            newy = hmax;
        }
        if (cury != newy) {
            articleView2.scrollTo(0, newy);
        }

}
Cool reader has very low page turn flicker, the second code I could not see in action.

In an e-ink friendly app, there would be an option to select high-contrast skins and optimize page refreshes.

Once the keyword "e-ink" or "e-ink friendly" will show up in the Android Market, then we've got the ball rolling.


Last edited by bardo; 01-20-2012 at 11:07 AM.
bardo is offline   Reply With Quote
Old 01-21-2012, 10:07 PM   #13
Morkl
Connoisseur
Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.
 
Posts: 80
Karma: 68347
Join Date: Oct 2009
Location: Sweden
Device: PRS-T1
Quote:
Originally Posted by bardo View Post
In order to see more e-ink friendly applications and to get app designers on board, we need a replacement class for the usual WebView.
It should simply be a matter of overriding the methods that have updateMode overloads - basically, copying the methods in the EinkListView I posted about except the constructors and then adding the WebView constructors and an mUpdateMode variable. Then the update mode can be controlled by changing the mUpdateMode variable.

Quote:
Being not an Android programmer, I looked around for generic code which could make it in such a replacement. So far I found these:

- The flicker-free page turn code used in cool reader:
credits to DairyKnight <dairyknight@gmail.com>
http://crengine.git.sourceforge.net/...ontroller.java

- Max Kammerer's code for his nook port of aarddict:
http:///github.com/max-kammerer/aarddict_nook
Code:
// from kbs - trook.projectsource code.

private final void pageUp() {
        int cury = articleView2.getScrollY();
        if (cury == 0) { return; }
        int newy = cury - WEB_SCROLL_PX;
        if (newy < 0) {
            newy = 0;
        }
        articleView2.scrollTo(0, newy);

}

private final void pageDown() {

        int cury = articleView2.getScrollY();
        int hmax = articleView2.getContentHeight() - 200;
        if (hmax < 0) {
            hmax = 0;
        }
        int newy = cury + WEB_SCROLL_PX;
        if (newy > hmax) {
            newy = hmax;
        }
        if (cury != newy) {
            articleView2.scrollTo(0, newy);
        }

}
Cool reader has very low page turn flicker, the second code I could not see in action.

In an e-ink friendly app, there would be an option to select high-contrast skins and optimize page refreshes.

Once the keyword "e-ink" or "e-ink friendly" will show up in the Android Market, then we've got the ball rolling.

The Cool Reader code looks like it does some device specific hardware magic to turn of the flashing, and the aardict code seems to be generic "page up"/"page down" code (i.e. it reduces superfluous flicker due to scrolling animations, but does not affect the way the screen behaves).

I guess the former is what everyone would like to be able to do, and the latter is something that should be the very minimal effort expected from a developer of an app if it is to be called E-ink friendly (i.e. avoiding animations of all kinds, aiming instead for static screens whenever possible).

Luckily, Sony has provided us with a higher level interface with the "update modes" in the android.view.View class, so on the PRS-T1 a non-flickery app doesn't have to worry about the hardware as long as it utilizes the update modes. It still goes in the former category though, since the methods are device-specific.

Anyhow, the source code for my ListView experiment is available at SourceForge if anyone wants to take a peek, and an APK is available here.
Morkl is offline   Reply With Quote
Old 01-23-2012, 06:58 AM   #14
bardo
Member
bardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enoughbardo will become famous soon enough
 
Posts: 17
Karma: 548
Join Date: Jan 2012
Device: PRS-T1
Thanks for providing sample code for android.view.View, I will try it out (still a way to go to set up the dev environment and start coding..)

Below is how Aardict (btw an amazing offline Wikipedia viewer including MathML!) actually does its page turn. Could the articleView.pageUp function work with your interface?

How to include this in the generic Aard Dicitonary code so that other non-Sony devices still work?

Thanks, Bardo

Code:
/* This file is part of Aard Dictionary for Android <http://aarddict.org>.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt>
 * for more details.
 * 
 * Copyright (C) 2010 Igor Tkach
*/

package aarddict.android;

import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;

class ArticleView extends WebView {

	interface ScrollListener {
		void onScroll(int l, int t, int oldl, int oldt);
	}
	
	private ScrollListener scrollListener;
	
	public ArticleView(Context context) {
		super(context);
	}

	public ArticleView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public ArticleView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		if (scrollListener != null) {
			scrollListener.onScroll(l, t, oldl, oldt);			
		}
	}

	public void setOnScrollListener(ScrollListener l) {
		this.scrollListener = l;
	}
}
Code:
package aarddict.android;

public class Article extends ArticleViewActivity {

}
Code:
/* This file is part of Aard Dictionary for Android <http://aarddict.org>.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt>
 * for more details.
 * 
 * Copyright (C) 2010 Igor Tkach
*/

package aarddict.android;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import aarddict.Article;
import aarddict.ArticleNotFound;
import aarddict.Entry;
import aarddict.LookupWord;
import aarddict.RedirectTooManyLevels;
import aarddict.Volume;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.Window;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;


public class ArticleViewActivity extends BaseDictionaryActivity {

    private final static String TAG = ArticleViewActivity.class.getName();
    
    private ArticleView         articleView;
    private String              sharedCSS;
    private String              mediawikiSharedCSS;
    private String              mediawikiMonobookCSS;
    private String              js;

    private List<HistoryItem>   backItems;
    private Timer               timer;
    private TimerTask           currentTask;
    private TimerTask           currentHideNextButtonTask;
    private AlphaAnimation 		fadeOutAnimation;
    private boolean 			useAnimation = false;        
    
	private Map<Article, ScrollXY> scrollPositionsH;
	private Map<Article, ScrollXY> scrollPositionsV;
	private boolean                saveScrollPos = true;
    
    
    static class AnimationAdapter implements AnimationListener {
		public void onAnimationEnd(Animation animation) {}
		public void onAnimationRepeat(Animation animation) {}
		public void onAnimationStart(Animation animation) {}    	
    }
    
    @Override
    void initUI() {
    	this.scrollPositionsH = Collections.synchronizedMap(new HashMap<Article, ScrollXY>());
    	this.scrollPositionsV = Collections.synchronizedMap(new HashMap<Article, ScrollXY>());
        loadAssets();                
        
        //Animation is broken before 2.1 - animation listener notified,
        //only sometimes so we can't use it
        try {
        	useAnimation = Integer.parseInt(Build.VERSION.SDK) > 6;
        }        
        catch (Exception e) {
        	Log.w(TAG, "Failed to parse SDK version string as int: " + Build.VERSION.SDK);	
        }

        Log.d(TAG, "Build.VERSION.SDK: " + Build.VERSION.SDK);
        Log.d(TAG, "use animation? " + useAnimation);
        
        fadeOutAnimation = new AlphaAnimation(1f, 0f);
        fadeOutAnimation.setDuration(600);
        fadeOutAnimation.setAnimationListener(new AnimationAdapter() {
        	public void onAnimationEnd(Animation animation) {
        		Button nextButton = (Button)findViewById(R.id.NextButton);
        		nextButton.setVisibility(Button.GONE);
        	}
        });        					        
        
        timer = new Timer();
        
        backItems = Collections.synchronizedList(new LinkedList<HistoryItem>());
        
        getWindow().requestFeature(Window.FEATURE_PROGRESS);        
        setContentView(R.layout.article_view);                                
        articleView = (ArticleView)findViewById(R.id.ArticleView);    
        
        articleView.setOnScrollListener(new ArticleView.ScrollListener(){
			public void onScroll(int l, int t, int oldl, int oldt) {
				saveScrollPos(l, t);
			}        	
        });
        
        articleView.getSettings().setJavaScriptEnabled(true);
        
        articleView.addJavascriptInterface(new SectionMatcher(), "matcher");
                                
        articleView.setWebChromeClient(new WebChromeClient(){
            
            @Override
            public boolean onJsAlert(WebView view, String url, String message,
            		JsResult result) {            	
            	Log.d(TAG + ".js", String.format("[%s]: %s", url, message));
            	result.cancel();
            	return true;
            }
            
            public void onProgressChanged(WebView view, int newProgress) {
                Log.d(TAG, "Progress: " + newProgress);
                setProgress(5000 + newProgress * 50);                
            }
        });
                            
        articleView.setWebViewClient(new WebViewClient() {
                    	        	
            @Override
            public void onPageFinished(WebView view, String url) {
                Log.d(TAG, "Page finished: " + url);
                currentTask = null;
                String section = null;
                                                
                if (url.contains("#")) {
                	LookupWord lookupWord = LookupWord.splitWord(url);                    
                    section = lookupWord.section;
                    if (backItems.size() > 0) {
                    	HistoryItem currentHistoryItem = backItems.get(backItems.size() - 1); 
                        HistoryItem h = new HistoryItem(currentHistoryItem);
                        h.article.section = section;
                        backItems.add(h);
                    }
                }
                else if (backItems.size() > 0) {
                    Article current = backItems.get(backItems.size() - 1).article;
                    section = current.section;
                }
                if (!restoreScrollPos()) {
                	goToSection(section);
                }
            }
            
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, final String url) {
                Log.d(TAG, "URL clicked: " + url);
                String urlLower = url.toLowerCase(); 
                if (urlLower.startsWith("http://") ||
                    urlLower.startsWith("https://") ||
                    urlLower.startsWith("ftp://") ||
                    urlLower.startsWith("sftp://") ||
                    urlLower.startsWith("mailto:")) {
                    Intent browserIntent = new Intent(Intent.ACTION_VIEW, 
                                                Uri.parse(url)); 
                    startActivity(browserIntent);                                         
                }
                else {
                	if (currentTask == null) {
                		currentTask = new TimerTask() {							
							public void run() {
								try {
									Article currentArticle = backItems.get(backItems.size() - 1).article;
									try {
									    Iterator<Entry> currentIterator = dictionaryService.followLink(url, currentArticle.volumeId);
	                                    List<Entry> result = new ArrayList<Entry>();
	                                    while (currentIterator.hasNext() && result.size() < 20) {
	                                        result.add(currentIterator.next());
	                                    }                                   
	                                    showNext(new HistoryItem(result));									    
									}
									catch (ArticleNotFound e) {
									    showMessage(getString(R.string.msgArticleNotFound, e.word.toString()));
									}
								}								
								catch (Exception e) {
									StringBuilder msgBuilder = new StringBuilder("There was an error following link ")
									.append("\"").append(url).append("\"");
									if (e.getMessage() != null) {
										msgBuilder.append(": ").append(e.getMessage());
									}									
									final String msg = msgBuilder.toString(); 
									Log.e(TAG, msg, e);
									showError(msg);
								}
							}
						};
						try {
						    timer.schedule(currentTask, 0);
						}
						catch (Exception e) {
						    Log.d(TAG, "Failed to schedule task", e);
						}
                	}                	
                }
                return true;
            }
        });
        final Button nextButton = (Button)findViewById(R.id.NextButton);
        nextButton.getBackground().setAlpha(180);
        nextButton.setOnClickListener(new View.OnClickListener() {			
			public void onClick(View v) {
				if (nextButton.getVisibility() == View.VISIBLE) {
					updateNextButtonVisibility();
					nextArticle();
					updateNextButtonVisibility();
				}											
			}
		});
		articleView.setOnTouchListener(
			new View.OnTouchListener() {				
				public boolean onTouch(View v, MotionEvent event) {
					updateNextButtonVisibility();
					return false;
				}
			}
		);
        setProgressBarVisibility(true);
    }

    private void scrollTo(ScrollXY s) {
    	scrollTo(s.x, s.y);
    }
    
    private void scrollTo(int x, int y) {
    	saveScrollPos = false; 
    	Log.d(TAG, "Scroll to " + x + ", " + y);
    	articleView.scrollTo(x, y);
    	saveScrollPos = true;
    }
    
    private void goToSection(String section) {
    	Log.d(TAG, "Go to section " + section);
    	if (section == null || section.trim().equals("")) {
    		scrollTo(0, 0);
    	}
    	else {
    		articleView.loadUrl(String.format("javascript:scrollToMatch(\"%s\")", section));
    	}
    }    
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:
                goBack();   
                break;
				
            case KeyEvent.KEYCODE_VOLUME_UP:
                if (!articleView.pageUp(false)) {
                    goBack();
                }
                break;
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                if (!articleView.pageDown(false)) {
                    nextArticle();
                };
                break;
				
			// add PRS-T1 hardware keys
			case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (!articleView.pageUp(false)) {
                    goBack();
                }
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (!articleView.pageDown(false)) {
                    nextArticle();
                };
                break;
				
				
            default:
                return super.onKeyDown(keyCode, event);
        }
        return true;
    }
    
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
    	//eat key ups corresponding to key downs so that volume keys don't beep
    switch (keyCode) {
        case KeyEvent.KEYCODE_BACK:
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_DOWN:
            break;
        default:
            return super.onKeyDown(keyCode, event);
        }
        return true;
    }
        
    private boolean zoomIn() {        
        boolean zoomed = articleView.zoomIn();
        float scale = articleView.getScale();
        articleView.setInitialScale(Math.round(scale*100));
        return zoomed;
    }
    
    private boolean zoomOut() {
        boolean zoomed = articleView.zoomOut();
        float scale = articleView.getScale();
        articleView.setInitialScale(Math.round(scale*100));
        return zoomed;    	
    }
        
    private void goBack() {
        if (backItems.size() == 1) {
            finish();
        }        
    	if (currentTask != null) {
    		return;
    	}
        if (backItems.size() > 1) {
            HistoryItem current = backItems.remove(backItems.size() - 1); 
            HistoryItem prev = backItems.get(backItems.size() - 1);
            
            Article prevArticle = prev.article; 
            if (prevArticle.equalsIgnoreSection(current.article)) {
            	resetTitleToCurrent();
            	if (!prevArticle.sectionEquals(current.article) && !restoreScrollPos()) {
            		goToSection(prevArticle.section);
            	}
            }   
            else {
            	showCurrentArticle();
            }
        }
    }
            
    private void nextArticle() {
    	HistoryItem current = backItems.get(backItems.size() - 1);
    	if (current.hasNext()) {
    		showNext(current);
    	}
    }
    
    @Override
    public boolean onSearchRequested() {
        finish();
        return true;
    }
    
    final static int MENU_VIEW_ONLINE = 1;
    final static int MENU_NEW_LOOKUP = 2;
    final static int MENU_ZOOM_IN = 3;
    final static int MENU_ZOOM_OUT = 4;
    
    private MenuItem miViewOnline; 
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        miViewOnline = menu.add(0, MENU_VIEW_ONLINE, 0, R.string.mnViewOnline).setIcon(android.R.drawable.ic_menu_view);
        menu.add(0, MENU_NEW_LOOKUP, 0, R.string.mnNewLookup).setIcon(android.R.drawable.ic_menu_search);        
        menu.add(0, MENU_ZOOM_OUT, 0, R.string.mnZoomOut).setIcon(R.drawable.ic_menu_zoom_out);
        menu.add(0, MENU_ZOOM_IN, 0, R.string.mnZoomIn).setIcon(R.drawable.ic_menu_zoom_in);
        return true;
    }
    
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
    	boolean enableViewOnline = false;
        if (this.backItems.size() > 0) {
            HistoryItem historyItem = backItems.get(backItems.size() - 1);
            Article current = historyItem.article;
            Volume d = dictionaryService.getVolume(current.volumeId);
            enableViewOnline = d.getArticleURLTemplate() != null;            
        }    	    
    	miViewOnline.setEnabled(enableViewOnline);
    	return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_VIEW_ONLINE:
            viewOnline();
            break;
        case MENU_NEW_LOOKUP:
            onSearchRequested();
            break;
        case MENU_ZOOM_IN:
            zoomIn();
            break;
        case MENU_ZOOM_OUT:
            zoomOut();
            break;
        default:
            return super.onOptionsItemSelected(item);
        }
        return true;
    }
        
    private void viewOnline() {
        if (this.backItems.size() > 0) {            
            Article current = this.backItems.get(this.backItems.size() - 1).article;
            Volume d = dictionaryService.getVolume(current.volumeId);
            String url = d == null ? null : d.getArticleURL(current.title);
            if (url != null) {
                Intent browserIntent = new Intent(Intent.ACTION_VIEW, 
                        Uri.parse(url)); 
                startActivity(browserIntent);                                         
            }
        }
    }
    
    private void showArticle(String volumeId, long articlePointer, String word, String section) {
        Log.d(TAG, "word: " + word);
        Log.d(TAG, "dictionaryId: " + volumeId);
        Log.d(TAG, "articlePointer: " + articlePointer);
        Log.d(TAG, "section: " + section);                       
        Volume d = dictionaryService.getVolume(volumeId);        
        Entry entry = new Entry(d.getId(), word, articlePointer);
        entry.section = section;        
        this.showArticle(entry);
    }
    
    private void showArticle(Entry entry) {        
        List<Entry> result = new ArrayList<Entry>();
        result.add(entry);
        
        try {
            Iterator<Entry> currentIterator = dictionaryService.followLink(entry.title, entry.volumeId);            
            while (currentIterator.hasNext() && result.size() < 20) {
                Entry next = currentIterator.next();
                if (!next.equals(entry)) {
                    result.add(next);
                }
            }                                                                                    
        }
        catch (ArticleNotFound e) {
            Log.d(TAG, String.format("Article \"%s\" not found - unexpected", e.word));
        }        
        showNext(new HistoryItem(result));
    }    

    private Map<Article, ScrollXY> getScrollPositions() {
		int orientation = getWindowManager().getDefaultDisplay().getOrientation();
		switch (orientation) {
			case Surface.ROTATION_0:
			case Surface.ROTATION_180:
				return scrollPositionsV;
			default:
				return scrollPositionsH;
		}
    }
    
    private void saveScrollPos(int x, int y) {
    	if (!saveScrollPos) {
    		//Log.d(TAG, "Not saving scroll position (disabled)");
    		return;
    	}
    	if (backItems.size() > 0) {
	    	Article a = backItems.get(backItems.size() - 1).article;
	    	Map<Article, ScrollXY> positions = getScrollPositions();
	    	ScrollXY s = positions.get(a);
	    	if (s == null) {
		    	s = new ScrollXY(x, y);
		    	positions.put(a, s);
	    	}
	    	else {
	    		s.x = x;
	    		s.y = y;
	    	}
	    	//Log.d(TAG, String.format("Saving scroll position %s for %s", s, a.title));
	    	getScrollPositions().put(a, s);    	
    	}
    }
    
    private boolean restoreScrollPos() {
    	if (backItems.size() > 0) { 
	    	Article a = backItems.get(backItems.size() - 1).article;    	
	    	ScrollXY s = getScrollPositions().get(a);
	    	if (s == null) {
	    		return false;
	    	}
	    	scrollTo(s);
	    	return true;
    	}
    	return false;
    }
    
    private void showNext(HistoryItem item_) {
    	final HistoryItem item = new HistoryItem(item_);
    	final Entry entry = item.next();
    	runOnUiThread(new Runnable() {
			public void run() {
				setTitle(item);
				setProgress(1000);
			}
		});    	
    	currentTask = new TimerTask() {
			public void run() {
		        try {
			        Article a = dictionaryService.getArticle(entry);			        			        
			        try {
			            a = dictionaryService.redirect(a);
			            item.article = new Article(a);
			        }            
			        catch (ArticleNotFound e) {
			            showMessage(getString(R.string.msgRedirectNotFound, e.word.toString()));
			            return;
			        }
			        catch (RedirectTooManyLevels e) {
			            showMessage(getString(R.string.msgTooManyRedirects, a.getRedirect()));
			            return;
			        }
			        catch (Exception e) {
			        	Log.e(TAG, "Redirect failed", e);
			            showError(getString(R.string.msgErrorLoadingArticle, a.title));
			            return;
			        }
			        
			        HistoryItem oldCurrent = null;
			        if (!backItems.isEmpty())
			        	oldCurrent = backItems.get(backItems.size() - 1);
			        
			        backItems.add(item);
			        
			        if (oldCurrent != null) {
			        	HistoryItem newCurrent = item;
			            if (newCurrent.article.equalsIgnoreSection(oldCurrent.article)) {
			                
			            	final String section = oldCurrent.article.sectionEquals(newCurrent.article) ? null : newCurrent.article.section;
			            	
			            	runOnUiThread(new Runnable() {								
								public void run() {
									resetTitleToCurrent();
									if (section != null) {
									    goToSection(section);
									}
									setProgress(10000);
									currentTask = null;
								}
							});			                
			            }   
			            else {
			            	showCurrentArticle();
			            }			        	
			        }
			        else {
			        	showCurrentArticle();
			        }			        			        							
		        }
		        catch (Exception e) {
		            String msg = getString(R.string.msgErrorLoadingArticle, entry.title);
		        	Log.e(TAG, msg, e);
		        	showError(msg);
		        }
			}
    	};
    	try {
    	    timer.schedule(currentTask, 0);
    	}
    	catch (Exception e) {
    	    Log.d(TAG, "Failed to schedule task", e);
    	}
    }
        
    private void showCurrentArticle() {
    	runOnUiThread(new Runnable() {			
			public void run() {		        
		        setProgress(5000);
		        resetTitleToCurrent();		       
		        Article a = backItems.get(backItems.size() - 1).article;
		        Log.d(TAG, "Show article: " + a.text);        
		        articleView.loadDataWithBaseURL("", wrap(a.text), "text/html", "utf-8", null);
			}
		});
    }
        
    private void updateNextButtonVisibility() {
    	if (currentHideNextButtonTask != null) {
    		currentHideNextButtonTask.cancel();
    		currentHideNextButtonTask = null;
    	}
    	boolean hasNextArticle = false;
        if (backItems.size() > 0) {
            HistoryItem historyItem = backItems.get(backItems.size() - 1);
            hasNextArticle = historyItem.hasNext();
        }
        final Button nextButton = (Button)findViewById(R.id.NextButton);
        if (hasNextArticle) {
        	if (nextButton.getVisibility() == View.GONE){
        		nextButton.setVisibility(View.VISIBLE);
        	}        	
        	currentHideNextButtonTask = new TimerTask() {			
        		@Override
        		public void run() {
        			runOnUiThread(new Runnable() {					
        				public void run() {
        	        		if (useAnimation) {
        	        			nextButton.startAnimation(fadeOutAnimation);
        	        		}
        	        		else {
        	        			nextButton.setVisibility(View.GONE);
        	        		}
        					currentHideNextButtonTask = null;
        				}
        			});			
        		}
        	}; 
        	try {
        		timer.schedule(currentHideNextButtonTask, 1800);        		
        	}
        	catch (IllegalStateException e) {
            	//this may happen if orientation changes while users touches screen               	
            	Log.d(TAG, "Failed to schedule \"Next\" button hide", e);        		
        	}
        }        
        else {
        	nextButton.setVisibility(View.GONE);
        }
    }
        
    private void showMessage(final String message) {
    	runOnUiThread(new Runnable() {
			public void run() {
		    	currentTask = null;
		    	setProgress(10000);
		    	resetTitleToCurrent();
		        Toast.makeText(ArticleViewActivity.this, message, Toast.LENGTH_LONG).show();
		        if (backItems.isEmpty()) {
		            finish();
		        }        				
			}
		});
    }

    private void showError(final String message) {
    	runOnUiThread(new Runnable() {
			public void run() {
		    	currentTask = null;
		    	setProgress(10000);
		    	resetTitleToCurrent();
		        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ArticleViewActivity.this);
		        dialogBuilder.setTitle(R.string.titleError).setMessage(message).setNeutralButton(R.string.btnDismiss, new OnClickListener() {            
		            public void onClick(DialogInterface dialog, int which) {
		                dialog.dismiss();
		                if (backItems.isEmpty()) {
		                    finish();
		                }
		            }
		        });
		        dialogBuilder.show();
			}
		});    	
    }
    
        
    private void setTitle(CharSequence articleTitle, CharSequence dictTitle) {
    	setTitle(getString(R.string.titleArticleViewActivity, articleTitle, dictTitle));
    }        
    
    private void resetTitleToCurrent() {    	    		
    	if (!backItems.isEmpty()) {
    		HistoryItem current = backItems.get(backItems.size() - 1);
    		setTitle(current);
    	}
    }
    
    private void setTitle(HistoryItem item) {		
		StringBuilder title = new StringBuilder();
		if (item.entries.size() > 1) {
			title
			.append(item.entryIndex + 1)
			.append("/")
			.append(item.entries.size())
			.append(" ");
		}
		Entry entry = item.current();
		title.append(entry.title);
		setTitle(title, dictionaryService.getDisplayTitle(entry.volumeId));    	
    }
    
    private String wrap(String articleText) {
        return new StringBuilder("<html>")
        .append("<head>")
        .append(this.sharedCSS)
        .append(this.mediawikiSharedCSS)
        .append(this.mediawikiMonobookCSS)
        .append(this.js)
        .append("</head>")
        .append("<body>")
        .append("<div id=\"globalWrapper\">")        
        .append(articleText)
        .append("</div>")
        .append("</body>")
        .append("</html>")
        .toString();
    }
    
    private String wrapCSS(String css) {
        return String.format("<style type=\"text/css\">%s</style>", css);
    }

    private String wrapJS(String js) {
        return String.format("<script type=\"text/javascript\">%s</script>", js);
    }
    
    private void loadAssets() {
        try {
            this.sharedCSS = wrapCSS(readFile("shared.css"));
            this.mediawikiSharedCSS = wrapCSS(readFile("mediawiki_shared.css"));
            this.mediawikiMonobookCSS = wrapCSS(readFile("mediawiki_monobook.css"));
            this.js = wrapJS(readFile("aar.js"));
        }
        catch (IOException e) {
            Log.e(TAG, "Failed to load assets", e);
        }        
    }
    
    private String readFile(String name) throws IOException {
        final char[] buffer = new char[0x1000];
        StringBuilder out = new StringBuilder();
        InputStream is = getResources().getAssets().open(name);
        Reader in = new InputStreamReader(is, "UTF-8");
        int read;
        do {
          read = in.read(buffer, 0, buffer.length);
          if (read>0) {
            out.append(buffer, 0, read);
          }
        } while (read>=0);
        return out.toString();
    }

    
    @Override
    protected void onPause() {
    	super.onPause();
    	SharedPreferences prefs = getPreferences(MODE_PRIVATE);
    	Editor e = prefs.edit();
    	e.putFloat("articleView.scale", articleView.getScale());
    	boolean success = e.commit();
    	if (!success) {
    		Log.w(TAG, "Failed to save article view scale pref");
    	}
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	SharedPreferences prefs = getPreferences(MODE_PRIVATE);    	
    	float scale = prefs.getFloat("articleView.scale", 1.0f);
    	int initialScale = Math.round(scale*100);
    	Log.d(TAG, "Setting initial article view scale to " + initialScale);
    	articleView.setInitialScale(initialScale);
    }
    
    @Override
    protected void onDestroy() {
    	super.onDestroy();
    	timer.cancel();
    	scrollPositionsH.clear();
    	scrollPositionsV.clear();
    	backItems.clear();
    }

    @Override
    void onDictionaryServiceReady() {
    	if (this.backItems.isEmpty()) {
	        final Intent intent = getIntent();	        
	        if (intent != null && intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEARCH)) {
	            final String word = intent.getStringExtra("query");
	            
	            if (currentTask != null) {
	                currentTask.cancel();	                
	            }
	            
	            currentTask = new TimerTask() {                    
                    @Override
                    public void run() {
                        setProgress(500);                                      
                        Log.d(TAG, "intent.getDataString(): " + intent.getDataString());
                        Iterator<Entry> results = dictionaryService.lookup(word);
                        Log.d(TAG, "Looked up " + word );
                        if (results.hasNext()) {
                            currentTask = null;
                            Entry entry = results.next();
                            showArticle(entry);
                        }
                        else {
                            showMessage(getString(R.string.msgArticleNotFound, word));
                        }
                    }
                };
	            
                try {
                    timer.schedule(currentTask, 0);
                }
                catch (Exception e) {
                    Log.d(TAG, "Failed to schedule task", e);
                    showError(getString(R.string.msgErrorLoadingArticle, word));
                }	            
	        }
	        else {
	            String word = intent.getStringExtra("word");                
	            String section = intent.getStringExtra("section");	        
	            String volumeId = intent.getStringExtra("volumeId");
	            long articlePointer = intent.getLongExtra("articlePointer", -1);
	            dictionaryService.setPreferred(volumeId);
	            showArticle(volumeId, articlePointer, word, section);
	        }
    	}
    	else {
    		showCurrentArticle();    		
    	}
    }    
    
    @SuppressWarnings("unchecked")
    @Override
    protected void onSaveInstanceState(Bundle outState) {
    	super.onSaveInstanceState(outState);
    	outState.putSerializable("backItems", new LinkedList(backItems));
    	outState.putSerializable("scrollPositionsH", new HashMap(scrollPositionsH));
    	outState.putSerializable("scrollPositionsV", new HashMap(scrollPositionsV));    	
    }
    
    @SuppressWarnings("unchecked")
	@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
    	super.onRestoreInstanceState(savedInstanceState);
    	backItems = Collections.synchronizedList((List)savedInstanceState.getSerializable("backItems"));
    	scrollPositionsH = Collections.synchronizedMap((Map)savedInstanceState.getSerializable("scrollPositionsH"));
    	scrollPositionsV = Collections.synchronizedMap((Map)savedInstanceState.getSerializable("scrollPositionsV"));
    }	
}

Last edited by bardo; 01-23-2012 at 08:58 AM.
bardo is offline   Reply With Quote
Old 01-27-2012, 07:51 AM   #15
Morkl
Connoisseur
Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.Morkl can talk to the animals.
 
Posts: 80
Karma: 68347
Join Date: Oct 2009
Location: Sweden
Device: PRS-T1
Quote:
Originally Posted by bardo View Post
Thanks for providing sample code for android.view.View, I will try it out (still a way to go to set up the dev environment and start coding..)

Below is how Aardict (btw an amazing offline Wikipedia viewer including MathML!) actually does its page turn. Could the articleView.pageUp function work with your interface?

How to include this in the generic Aard Dicitonary code so that other non-Sony devices still work?

Thanks, Bardo

Code:
/* This file is part of Aard Dictionary for Android <http://aarddict.org>.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt>
 * for more details.
 * 
 * Copyright (C) 2010 Igor Tkach
*/

package aarddict.android;

import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;

class ArticleView extends WebView {

	interface ScrollListener {
		void onScroll(int l, int t, int oldl, int oldt);
	}
	
	private ScrollListener scrollListener;
	
	public ArticleView(Context context) {
		super(context);
	}

	public ArticleView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public ArticleView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		if (scrollListener != null) {
			scrollListener.onScroll(l, t, oldl, oldt);			
		}
	}

	public void setOnScrollListener(ScrollListener l) {
		this.scrollListener = l;
	}
}
It should just be a matter of including the aforementioned methods and variable in the ArticleView class and keeping track of the update mode variable:

Code:
class ArticleView extends WebView {
	private int mUpdateMode = 3;
	
	public void setUpdateMode(int m) {
		this.mUpdateMode = m;
	}
	
	interface ScrollListener {
		void onScroll(int l, int t, int oldl, int oldt);
	}
	
	private ScrollListener scrollListener;
	
	public ArticleView(Context context) {
		super(context);
	}

	public ArticleView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public ArticleView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		if (scrollListener != null) {
			scrollListener.onScroll(l, t, oldl, oldt);			
		}
	}

	public void setOnScrollListener(ScrollListener l) {
		this.scrollListener = l;
	}

	@Override
	public void invalidate() {
		super.invalidate(mUpdateMode);
	}

	@Override
	public void invalidate(int l, int t, int r, int b) {
		super.invalidate(l, t, r, b, mUpdateMode);
	}

	@Override
	public void invalidate(Rect dirty) {
		super.invalidate(dirty, mUpdateMode);
	}

	@Override
	public void invalidateDrawable(Drawable drawable) {
		super.invalidateDrawable(drawable, mUpdateMode);
	}

	@Override
	public void postInvalidate() {
		super.postInvalidate(mUpdateMode);
	}

	@Override
	public void postInvalidate(int left, int top, int right, int bottom) {
		super.postInvalidate(left, top, right, bottom, mUpdateMode);
	}

	@Override
	public void postInvalidateDelayed(long delayMilliseconds, int left,
			int top, int right, int bottom) {
		super.postInvalidateDelayed(delayMilliseconds, left, top, right, bottom, mUpdateMode);
	}

	@Override
	public void postInvalidateDelayed(long delayMilliseconds) {
		super.postInvalidateDelayed(delayMilliseconds, mUpdateMode);
	}

	@Override
	public void scrollBy(int x, int y) {
		super.scrollBy(x, y, mUpdateMode);
	}

	@Override
	public void scrollTo(int x, int y) {
		super.scrollTo(x, y, mUpdateMode);
	}
}
For it to work with other devices, I guess you would have to make sure the standard methods are called instead of the special ones when running on any other device, i.e. something like:

Code:
	@Override
	public void invalidate() {
		if (It's.A.Sony()) {
			super.invalidate(mUpdateMode);
		} else super.invalidate();
	}
I think the dalvik vm only breaks down if it tries to call a method that isn't defined; not when it is referenced but never accessed. Can't verify this right now though.
Morkl is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
USB Host Mode (Master Mode) on K3 ericepe Kindle Developer's Corner 1 01-24-2012 04:59 AM
Request Safe mode or recovery mode for Pocket Edge? felixblackcat enTourage eDGe 25 01-08-2012 05:07 PM
What Does Everyone think of the K3 refresh Scarpad Amazon Kindle 6 08-29-2010 10:00 PM
After refresh maxhyl iRex 1 11-23-2008 03:50 AM


All times are GMT -4. The time now is 05:20 AM.


MobileRead.com is a privately owned, operated and funded community.