2013年6月28日 星期五

android : 確認google定位權限

原文參考在此
===================跳去權限設定畫面=======================
註:
Explanation of above code.

1. First we need to create LocationManager.

LocationManager lm = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
2. using isProviderEnabled Check location providers are already enabled or not.
  1. private void checkLocationProviders(){  
  2.     //String provider = Settings.Secure.getString(getContentResolver(),Settings.Secure.LOCATION_PROVIDERS_ALLOWED);  
  3.        if(lm.isProviderEnabled(LocationManager.GPS_PROVIDER)){  
  4.              
  5.            Toast.makeText(EnableLocationActivity.this"GPS provider Enabled: ",Toast.LENGTH_LONG).show();  
  6.           
  7.        }else if(lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){  
  8.              
  9.            Toast.makeText(EnableLocationActivity.this"Network provider Enabled: ",Toast.LENGTH_LONG).show();  
  10.              
  11.        }else{  
  12.            AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  13.            builder.setMessage("Location providers are not available. Enable GPS or network providers.")  
  14.                   .setCancelable(false)  
  15.                   .setPositiveButton("Yes"new DialogInterface.OnClickListener() {  
  16.                       public void onClick(DialogInterface dialog, int id) {  
  17.                           Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);  
  18.                       startActivityForResult(intent, 1);  
  19.                       }  
  20.                   })  
  21.                   .setNegativeButton("No"new DialogInterface.OnClickListener() {  
  22.                       public void onClick(DialogInterface dialog, int id) {  
  23.                           EnableLocationActivity.this.finish();  
  24.                       }  
  25.                   }).show();  
  26.              
  27.              
  28.        }  
  29.   
  30.    }  
  31.      
  32.    @Override  
  33. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  34.        checkLocationProviders();  
  35. super.onActivityResult(requestCode, resultCode, data);  
  36. }  
以下原文參考

=========設定好之後,可以進行Google location功能了=================================
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Getting Google Play availability status
    int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());

    // Showing status
    if(status!=ConnectionResult.SUCCESS){ // Google Play Services are not available

        int requestCode = 10;
        Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);
        dialog.show();

    }else { // Google Play Services are available

        // Getting reference to the SupportMapFragment of activity_main.xml
        SupportMapFragment fm = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);

        // Getting GoogleMap object from the fragment
        googleMap = fm.getMap();

        // Enabling MyLocation Layer of Google Map
        googleMap.setMyLocationEnabled(true);

        // Getting LocationManager object from System Service LOCATION_SERVICE
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        // Creating a criteria object to retrieve provider
        Criteria criteria = new Criteria();

        // Getting the name of the best provider
        String provider = locationManager.getBestProvider(criteria, true);

        // Getting Current Location
        Location location = locationManager.getLastKnownLocation(provider);

        LocationListener locationListener = new LocationListener() {
          void onLocationChanged(Location location) {
          // redraw the marker when get location update.
          drawMarker(location);
        }

        if(location!=null){
           //PLACE THE INITIAL MARKER              
           drawMarker(location);
        }
        locationManager.requestLocationUpdates(provider, 20000, 0, locationListener);
    }
}

private void drawMarker(Location location){
googleMap.clear();
LatLng currentPosition = new LatLng(location.getLatitude(),
location.getLongitude());
googleMap.addMarker(new MarkerOptions()
.position(currentPosition)
.snippet("Lat:" + location.getLatitude() + "Lng:"+ location.getLongitude()));
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
    .title("ME"));
}

2013年6月24日 星期一

如何從android client post中文到server去?

Just give me the code!!
==============================
....
....
....
 HttpPost httppost = new HttpPost("http://"+SERVER_ADDRESS+destPath);
       // Add your param
   java.util.Iterator<String> keys= keyAndValue.keySet().iterator();
       List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
   while(keys.hasNext()){
    String key=keys.next();
    String value=keyAndValue.get(key);
       nameValuePairs.add(new BasicNameValuePair(key, value));
   }
       httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs,HTTP.UTF_8));
       
       // Execute HTTP Post Request
       HttpResponse response = httpclient.execute(httppost);
     
       BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
       String str=reader.readLine();
   ...
  ....
  ...
==============================================================

2013年6月22日 星期六

for android : 最小單位的TabHost & Tabspec 框架

 layout---「activity_store_unit.xml」的內容
================================================
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/TabHost1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#EF7B2F"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".StoreProdListActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </TabWidget>

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:id="@+id/tab1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal" >

            </LinearLayout>

            <LinearLayout
                android:id="@+id/tab2"
                android:layout_width="match_parent"
                android:layout_height="match_parent" 
                android:orientation="horizontal">
            </LinearLayout>

            <LinearLayout
                android:id="@+id/tab3"
                android:layout_width="match_parent"
                android:layout_height="match_parent" 
                android:orientation="horizontal">
            </LinearLayout>
        </FrameLayout>
    </LinearLayout>

</TabHost>
==============================================

*.java
============================================
package com.neo_tech.goingtvmall.frontend;

import android.os.Bundle;
import android.app.Activity;
import android.app.LocalActivityManager;
import android.app.TabActivity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TabWidget;

public class StoreUnitActivity extends Activity {
              //extends TabActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_store_unit);
TabHost tabHost=(TabHost)findViewById(R.id.TabHost1);
   //tabHost.setup();
LocalActivityManager mLocalActivityManager = new LocalActivityManager(this, false);
mLocalActivityManager.dispatchCreate(savedInstanceState);
tabHost.setup(mLocalActivityManager);
TabSpec tbs=tabHost.newTabSpec("tab1");
    tbs.setIndicator("店家資訊");
    tbs.setContent(new Intent(this,
StoreIntroductionActivity.class ));
    try{
    tabHost.addTab(tbs);
tabHost.addTab(
tabHost.newTabSpec("tab2").setIndicator("有什麼產品呢?").setContent(new Intent(this,
StoreProdListActivity.class ))
);
tabHost.addTab(
tabHost.newTabSpec("tab3").setIndicator("選了什麼?").setContent(new Intent(this,
StoreCartActivity.class ))
);
   
    }catch(Exception exp){
                   exp.printStackTrace();
                   Log.e("error",exp.getMessage());
    }
   
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.store_unit, menu);
return true;
}

}
================================================

2013年6月17日 星期一

for android:分頁式ListView絕佳範例

原碼出處佇這
================================
package de.bsd.zwitscher;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ImageButton;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import de.bsd.zwitscher.account.AccountHolder;
import de.bsd.zwitscher.helper.FlushQueueTask;
import de.bsd.zwitscher.helper.MetaList;
import de.bsd.zwitscher.helper.NetworkHelper;
import de.bsd.zwitscher.other.TweetMarkerSync;
import twitter4j.*;
/**
* Show the list of tweets.
* To unify things a bit, we introduce pseudo list ids for timelines that are not lists:
* <ul>
* <li>0 : home/friends timeline</li>
* <li>-1 : mentions </li>
* <li>-2 : direct </li>
* <li>&gt;0 : saved search</li>
* </ul>
* @author Heiko W. Rupp
*/
public class TweetListActivity extends AbstractListActivity implements AbsListView.OnScrollListener,
        OnItemClickListener, OnItemLongClickListener {
    List<Status> statuses;
    List<DirectMessage> directs;
    List<Status> tweets;
    int list_id;
    ListView lv;
    Long userId=null;
    int userListId = -1;
    private Pattern filterPattern;
    int unreadCount = -1;
    /**
* Called when the activity is first created.
*/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Activity theParent = getParent();
        if ((!(theParent instanceof TabWidget)) && (android.os.Build.VERSION.SDK_INT<11)) {
            // We have no enclosing TabWidget, so we need to request the custom title
            requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
        }
        if ((!(theParent instanceof TabWidget)) && (android.os.Build.VERSION.SDK_INT>=11)) {
            // We have no enclosing TabWidget, so we need to request progress thingy
            requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        }
        // Set the layout of the list activity
        setContentView(R.layout.tweet_list_layout);
        // Get the windows progress bar from the enclosing TabWidget
        if ((!(theParent instanceof TabWidget)) && (android.os.Build.VERSION.SDK_INT<11)) {
            // We have no enclosing TabWidget, so we need our window here
            getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.window_title);
            progressBar = (ProgressBar) findViewById(R.id.title_progress_bar);
            titleTextBox = (TextView) findViewById(R.id.title_msg_box);
            ImageButton imageButton = (ImageButton) findViewById(R.id.back_button);
            imageButton.setVisibility(View.VISIBLE);
        }
        lv = getListView();
        lv.setItemsCanFocus(false);
        lv.setOnScrollListener(this);
lv.setOnItemClickListener(this);
lv.setOnItemLongClickListener(this); // Directly got to reply
        Bundle intentInfo = getIntent().getExtras();
        if (intentInfo==null) {
            list_id = 0;
        } else {
            userListId = intentInfo.getInt("userListid");
            list_id = intentInfo.getInt(TabWidget.LIST_ID);
            if (intentInfo.containsKey("userId")) {
                // Display tweets of a single user
                userId = intentInfo.getLong("userId");
                // This is a one off list. So don't offer the reload button
                ImageButton tweet_list_reload_button = (ImageButton) findViewById(R.id.tweet_list_reload_button);
                if (tweet_list_reload_button!=null)
                    tweet_list_reload_button.setVisibility(View.INVISIBLE);
            }
            if (intentInfo.containsKey("unreadCount")) {
                unreadCount = intentInfo.getInt("unreadCount");
            }
        }
        // Only get tweets from db to speed things up at start
        boolean fromDbOnly = tdb.getLastRead(account.getId(), list_id) != -1;
        // Check if we are switching accounts and the user wants to load messages from remote
        AccountHolder accountHolder = AccountHolder.getInstance(this);
        if (accountHolder.isSwitchingAccounts()) {
            accountHolder.setSwitchingAccounts(false); // reset flag
            // Ok, we are switching. Now check preferences
            SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
            boolean loadOnAccountChange = preferences.getBoolean("load_on_account_switch",false);
            if (loadOnAccountChange) { // preferences say ok to load
                fromDbOnly = false;
            }
        }
        // Start loading messages to be displayed
        fillListViewFromTimeline(fromDbOnly);
    }
    @Override
    public void onResume() {
      super.onResume();
        lv = getListView();
        lv.setOnScrollListener(this);
lv.setOnItemClickListener(this);
lv.setOnItemLongClickListener(this); // Directly got to reply
        String msg = setupFilter();
        if (msg!=null)
            Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
    }
    /**
* Handle click on a list item which triggers the detail view of that item
* @param parent Parent view
* @param view Clicked view
* @param position Position in the list that was clicked
* @param id row Id of the item that was clicked
*/
    public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
        if (statuses!=null) {
         Intent i = new Intent(this,OneTweetActivity.class);
         i.putExtra(getString(R.string.status), statuses.get(position));
         startActivity(i);
        }
        else if (directs!=null) {
           Intent i = new Intent(this, NewTweetActivity.class);
           i.putExtra("user",directs.get(position).getSender());
           i.putExtra("op",getString(R.string.direct));
           startActivity(i);
        } else {
            // Tweets; TODO
        }
    }
    /**
* Handle a long click on a list item - by directly jumping into reply mode
* @param parent Parent view
* @param view Clicked view
* @param position Position in the List that was clicked
* @param id row id of the item that was clicked
* @return true as the click was consumed
*/
    public boolean onItemLongClick(AdapterView<?> parent, View view,
            int position, long id) {
        Log.i("TLA","Long click, pos=" + position + ",id="+id);
        Intent i = new Intent(this, NewTweetActivity.class);
        if (statuses!=null) {
           i.putExtra(getString(R.string.status), statuses.get(position));
           i.putExtra("op",getString(R.string.reply));
           startActivity(i);
        }
        else if (directs!=null) {
         i.putExtra("user",directs.get(position).getSender());
         i.putExtra("op",getString(R.string.direct));
         startActivity(i);
        } else {
            // Tweets TODO
        }
        return true; // We've consumed the long click
    }
    /**
* Retrieve a list of statuses. Depending on listId, this is taken from
* different sources:
* <ul>
* <li>0 : home timeline</li>
* <li>-1 : mentions</li>
* <li>>0 : User list</li>
* </ul>
* This method may trigger a network call if fromDbOnly is false.
* The filter if not null is a regular expression, that if matches filters the
* tweet.
*
*
*
* @param fromDbOnly If true only statuses already in the DB are returned
* @param listId Id of the list / timeline to fetch (see above)
* @param updateStatusList Should the currently displayed list be updated?
* @return List of status items along with some counts
*/
private MetaList<Status> getTimlinesFromTwitter(boolean fromDbOnly, int listId,
                                                    boolean updateStatusList) {
Paging paging = new Paging();
MetaList<Status> myStatuses;
     long last = tdb.getLastRead(account.getId(), listId);
     if (last>0 )//&& !Debug.isDebuggerConnected())
     paging.sinceId(last).setCount(200);
        else
            paging.setCount(50); // 50 Tweets if we don't have the timeline yet
        switch (listId) {
        case 0:
            // Home time line
         myStatuses = th.getTimeline(paging,listId, fromDbOnly);
         break;
        case -1:
         myStatuses = th.getTimeline(paging, listId, fromDbOnly);
         break;
        case -2:
            // see below at getDirectsFromTwitter
            myStatuses = new MetaList<Status>();
            break;
        case -3:
            myStatuses = th.getTimeline(paging,listId,fromDbOnly);
            break;
        case -4:
            myStatuses = th.getTimeline(paging,listId,fromDbOnly);
            break;
        default:
         myStatuses = th.getUserList(paging,listId, fromDbOnly, unreadCount);
            if (unreadCount>-1) {
                List<Status> list = myStatuses.getList();
                if (list.size()<=unreadCount)
                    unreadCount = list.size()-1;
                if (unreadCount > -1)
                    last = list.get(unreadCount).getId();
            }
         break;
        }
        long newLast=-1;
        // Update the 'since' id in the database
     if (myStatuses.getList().size()>0) {
     newLast = myStatuses.getList().get(0).getId(); // assumption is that twitter sends the newest (=highest id) first
     tdb.updateOrInsertLastRead(account.getId(), listId, newLast);
     }
        // Sync with TweetMarker
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        boolean doTweetMarkerSync = prefs.getBoolean("tweetmarker_sync",false);
        long newLast2=-1;
        if (doTweetMarkerSync && listId>=0 && !account.isStatusNet() && !fromDbOnly) {
            if (listId==0)
                newLast2 = TweetMarkerSync.syncFromTweetMarker("timeline", account.getName());
            else
                newLast2 = TweetMarkerSync.syncFromTweetMarker("lists."+listId, account.getName());
            if (newLast2>newLast) {
                tdb.updateOrInsertLastRead(account.getId(), listId, newLast2);
            } else {
                if (listId==0)
                    TweetMarkerSync.syncToTweetMarker("timeline",newLast,account.getName(),th.getOAuth());
                else
                    TweetMarkerSync.syncToTweetMarker("lists."+listId,newLast,account.getName(),th.getOAuth());
            }
        }
        MetaList<Status> metaList;
        if (updateStatusList) {
            statuses = new ArrayList<Status>();
            List<Status> data = new ArrayList<Status>(myStatuses.getList().size());
            if (filterPattern==null) {
                setupFilter(); // TODO report errors?
            }
            for (Status status : myStatuses.getList()) {
                boolean shouldFilter = matchesFilter(status);
                if (shouldFilter) {
                    Log.i("TweetListActivity::filter, filtered ",status.getUser().getScreenName() + " - " + status.getText());
                } else {
                    data.add(status);
                    statuses.add(status);
                }
            }
            metaList = new MetaList<Status>(data,myStatuses.getNumOriginal(),myStatuses.getNumAdded());
        }
        else {
            metaList = new MetaList<Status>(new ArrayList<Status>(),0,0);
        }
        if (newLast2>last) {
            metaList.oldLast=newLast2;
            // the read status from remote is newer than the last read locally, so lets mark those in between as read
            Set<Long> ids = new HashSet<Long>(statuses.size());
            for (Status s : statuses) {
                long id = s.getId();
                if (id>last) {
// th.markStatusAsOld(id);
                    ids.add(id);
                }
            }
            th.markStatusesAsOld(ids);
        }
        else {
            metaList.oldLast = last;
        }
        for (Status status:metaList.getList()) {
            AccountHolder accountHolder = AccountHolder.getInstance(this);
            accountHolder.addUserName(status.getUser().getScreenName());
            if (status.getHashtagEntities()!=null) {
                for (HashtagEntity hte : status.getHashtagEntities()) {
                    accountHolder.addHashTag(hte.getText());
                }
            }
        }
        return metaList;
}
    /**
* Does the passed status match the filter pattern?
* This method checks the status text and the expanded url entities
* @param status Status to check
* @return True if the filter expression matches, false otherwise
*/
    private boolean matchesFilter(Status status) {
        boolean shouldFilter = false;
        if (filterPattern != null) {
            Matcher m = filterPattern.matcher(status.getText());
            if (m.matches())
                shouldFilter = true;
            if (status.getURLEntities() != null) {
                for (URLEntity ue : status.getURLEntities()) {
                    String expUrl = ue.getExpandedURL();
                    if (expUrl != null) {
                        m = filterPattern.matcher(expUrl);
                        if (m.matches())
                            shouldFilter = true;
                    }
                }
            }
        }
        return shouldFilter;
    }
    private MetaList<DirectMessage> getDirectsFromTwitter(boolean fromDbOnly) {
        MetaList<DirectMessage> messages;
        long last = tdb.getLastRead(account.getId(), -2);
        Paging paging = new Paging();
        if (last>-1)
            paging.setSinceId(last);
        messages = th.getDirectMessages(fromDbOnly, paging);
        directs = messages.getList();
        return messages;
    }
    private MetaList<Status> getSavedSearchFromTwitter(int searchId, boolean fromDbOnly) {
        MetaList<Status> messages;
        Paging paging = new Paging();
        paging.setCount(20);
        messages = th.getSavedSearchesTweets(searchId, fromDbOnly,paging);
        tweets = messages.getList();
        return messages;
    }
    public boolean onCreateOptionsMenu(Menu menu) {
        Activity theParent = getParent();
        if ((!(theParent instanceof TabWidget) && Build.VERSION.SDK_INT>=11)) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.list_activity_menu_honey,menu);
            ActionBar actionBar = this.getActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
            return true;
        }
        else if (theParent instanceof TabWidget) {
            return theParent.onCreateOptionsMenu(menu);
        }
        return false;
    }
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                break;
            case R.id.refresh:
                reload(null);
                break;
            case R.id.to_top:
                scrollToTop(null);
                break;
            case R.id.send:
                post(null);
                break;
            default:
                Log.i("TweetListActivity","Unknown item " + item);
        }
        return super.onOptionsItemSelected(item);
    }
    @Override
    protected void onPause() {
        super.onPause();
        if (getListAdapter() instanceof StatusAdapter) {
            StatusAdapter adapter = (StatusAdapter) getListAdapter();
            th.markStatusesAsOld(adapter.newOlds);
        }
    }
    /**
* Called from the reload button
* @param v view that was pressed
*/
    @SuppressWarnings("unused")
    public void reload(View v) {
        fillListViewFromTimeline(false);
        // Now check and process items that were created while we were offline
        new FlushQueueTask(this, account).execute();
        // TODO this is not really executed in parallel and seems to block the update of the
        // TODO main timeline
        // TODO the main timeline updates quicky and terminates when teh spinner thingy hides
        // TODO but if the next code is also executed, the timeline is only refreshed after
        // TODO this is all done
        // TODO this may be a fight for the tweets table ?
        NetworkHelper networkHelper = new NetworkHelper(this);
        if (list_id == 0 && networkHelper.mayReloadAdditional()) {
            System.out.println("### triggering GTLT -1");
            new GetTimeLineTask(this,-1,false, 5).execute(false);
            System.out.println("### triggering GTLT -2");
            new GetTimeLineTask(this,-2,false, 3).execute(false);
            System.out.println("### triggering done");
        }
    }
    private void fillListViewFromTimeline(boolean fromDbOnly) {
     new GetTimeLineTask(this,list_id,true, 0).execute(fromDbOnly);
    }
    public void onScrollStateChanged(AbsListView absListView, int i) {
        // nothing to do for us
    }
    @SuppressWarnings("unchecked")
    public void onScroll(AbsListView absListView, int firstVisible, int visibleCount, int totalCount) {
        boolean loadMore = /* maybe add a padding */
            firstVisible + visibleCount >= totalCount-1;
        // TODO if this is the very first load of a timeline, loadMore will not reveal
        // new items and just create load -- in this case skip it
        // TODO introduce a flag for this. Can perhaps be populated from the
        // knowledge of the stored last read id of the list
// Log.d("onScroll:","loadMore= " + loadMore + " f=" + firstVisible + ", vc=" + visibleCount + ", tc=" +totalCount);
        if(loadMore) {
//Debug.startMethodTracing("list" + firstVisible);
            ListAdapter adapter = absListView.getAdapter();
            if (adapter instanceof StatusAdapter) {
                StatusAdapter sta = (StatusAdapter) adapter;
                if (totalCount>0) {
                    Object item = sta.getItem(totalCount - 1);
                    int i = 0;
                    if (item instanceof DirectMessage) {
                        DirectMessage message = (DirectMessage) item;
                        List<DirectMessage> messages = th.getDirectsFromDb(message.getId(),7);
                        if (messages.size()>0) {
                            for (DirectMessage direct : messages) {
                                sta.insert(direct, totalCount + i);
                                directs.add(direct);
                                i++;
                            }
                            sta.notifyDataSetChanged();
                        }
                    } else if (item instanceof Status) {
                        Status last = (Status) item;
                        if (statuses==null)
                            statuses = new ArrayList<Status>();
                        List<Status> newStatuses = th.getStatuesFromDb(last.getId(),7,list_id);
                        // TODO add checking for old
                        if (newStatuses.size()>0) {
                            List<Long> readIds = obtainReadIds(newStatuses);
                            sta.readIds.addAll(readIds);
                            for (Status status : newStatuses ) {
                                if (!matchesFilter(status)) {
                                    sta.insert(status, totalCount + i);
                                    statuses.add(status);
                                    i++;
                                }
                            }
                            sta.notifyDataSetChanged();
                        }
                    }
                }
            }
//Debug.stopMethodTracing();
        }
    }
    private class GetTimeLineTask extends AsyncTask<Boolean, String, MetaList> {
        boolean fromDbOnly = false;
        String updating;
        Context context;
        private int listId;
        private boolean updateListAdapter;
        private int startDelaySecs;
        /**
* Fetch a timeline from db and/or remote. Allow to delay this a bit so that the
* progress bar etc. from earlier fetch tasks can be displayed.
* @param context Calling activity
* @param listId What timeline to fetch
* @param updateListAdapter Should the adapter be updated - true for current tab, false for others
* @param startDelaySecs How long to delay before the network fetch is performed.
*/
        private GetTimeLineTask(Context context, int listId, boolean updateListAdapter, int startDelaySecs) {
            this.context = context;
            this.listId = listId;
            this.updateListAdapter = updateListAdapter;
            this.startDelaySecs = startDelaySecs;
        }
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            updating = context.getString(R.string.updating);
            String s = getString(R.string.getting_statuses, account.getStatusType());
            if (updateListAdapter) {
                if (progressBar!=null) {
                    progressBar.setVisibility(ProgressBar.VISIBLE);
                }
            }
            if(titleTextBox!=null) {
                titleTextBox.setText(s);
            }
            if (Build.VERSION.SDK_INT>=11) {
                ActionBar ab = getActionBar();
                if (ab!=null) {
                    ab.setSubtitle(s);
                }
                if (getParent()!=null) {
                    getParent().setProgressBarIndeterminateVisibility(true);
                }
                else {
                    // No parent tab bar when used on a list
                    setProgressBarIndeterminateVisibility(true);
                }
            }
        }
@Override
        @SuppressWarnings("unchecked")
protected MetaList doInBackground(Boolean... params) {
            fromDbOnly = params[0];
            if (!fromDbOnly) {
                NetworkHelper networkHelper = new NetworkHelper(context);
                if (!networkHelper.isOnline()) {
                    // User wants stuff from the server, but we are offline
                    // so "fail fast"
                    fromDbOnly=true;
                }
            }
            if (startDelaySecs>0 && !fromDbOnly) {
                try {
                    Thread.sleep(1000L*startDelaySecs);
                } catch (InterruptedException e) {
                    Log.d("GetTimeLineTask",e.getMessage());
                }
            }
MetaList data;
            if (userId!=null) {
                List<twitter4j.Status> statuses = th.getUserTweets(userId);
                data = new MetaList(statuses,statuses.size(),0);
                String user = context.getString(R.string.user);
                publishProgress(user);
            }
            else {
                String directsString = context.getString(R.string.direct);
                if (this.listId >-5 && this.listId !=-2) {
                    String updating;
                    switch (listId) {
                        case 0: updating = context.getString(R.string.home_timeline);
                            break;
                        case -1: updating = context.getString(R.string.mentions);
                            break;
                        case -3: updating = context.getString(R.string.sent);
                            break;
                        case -4: updating = context.getString(R.string.favorites);
                            break;
                        default: updating = "";
                    }
                    publishProgress(updating);
                    data = getTimlinesFromTwitter(fromDbOnly, listId, updateListAdapter);
                }
                else if (listId ==-2) {
                    publishProgress(directsString);
                    data = getDirectsFromTwitter(fromDbOnly);
                }
                else { // list id < -4 ==> saved search
                    String s = context.getString(R.string.searches);
                    publishProgress(s);
                    data = getSavedSearchFromTwitter(-listId,fromDbOnly);
                }
            }
return data;
}
        @SuppressWarnings("unchecked")
@Override
protected void onPostExecute(MetaList result) {
            if (getListAdapter()!=null && getListAdapter().getCount()>0 && result.getNumAdded()==0) {
                // No new items, no need to replace the current adapter
                updateListAdapter=false;
            }
            if (updateListAdapter) {
                if (listId <-4) { // saved search
                    setListAdapter(new TweetAdapter(context, account, R.layout.tweet_list_item, result.getList()));
                }
                else if (listId == -2) { // direct messages
                    setListAdapter(new StatusAdapter(context, account, R.layout.tweet_list_item,
                                                result.getList(),0, new ArrayList<Long>()));
                }
                else { // all others
                    List<twitter4j.Status> statusList = (List<twitter4j.Status>) result.getList();
                    // Get the old adapter if it existed, get the read ids from it and persist them
                    if (getListAdapter()!=null) {
                        StatusAdapter oldOne = (StatusAdapter) getListAdapter();
                        Set<Long> newReadIds = oldOne.newOlds;
                        th.markStatusesAsOld(newReadIds);
                    }
                    List<Long> reads = obtainReadIds(statusList);
                    setListAdapter(new StatusAdapter(context, account, R.layout.tweet_list_item,
                            result.getList(), result.oldLast, reads));
                }
                if (result.getList().size()==0) {
                    Toast.makeText(context, getString(R.string.no_result), Toast.LENGTH_LONG).show();
                }
            }
            if (titleTextBox!=null)
                titleTextBox.setText(account.getAccountIdentifier());
            if (Build.VERSION.SDK_INT>=11) {
                ActionBar ab = getActionBar();
                if (ab!=null) {
                    String s=null;
                    Map<Integer,Pair<String,String>> userLists = tdb.getLists(account.getId());
                    if (userListId !=-1) {
                        Pair<String,String> nameOwnerPair = userLists.get(userListId);
                        if (nameOwnerPair!=null) {
                            String tmp;
                            if (nameOwnerPair.second.equals(account.getName()))
                                tmp = nameOwnerPair.first;
                            else
                                tmp = "@" +nameOwnerPair.second + "/" + nameOwnerPair.first;
                            s =tmp;
                        }
                    }
                    ab.setTitle(account.getAccountIdentifier());
                    ab.setSubtitle(s);
                }
            }
            if (updateListAdapter)
getListView().requestLayout();
            if (result.getNumOriginal()>0) { // TODO distinguish the timelines
                String tmp = context.getResources().getQuantityString(R.plurals.new_entries,result.getNumOriginal());
                String updating;
                switch (listId) {
                    case 0: updating = context.getString(R.string.home_timeline);
                        break;
                    case -1: updating = context.getString(R.string.mentions);
                        break;
                    case -2 : updating = context.getString(R.string.direct);
                        break;
                    case -3: updating = context.getString(R.string.sent);
                        break;
                    case -4: updating = context.getString(R.string.favorites);
                        break;
                    default: updating = context.getString(R.string.list);
                }
                if (listId!=-2) { // direct messages produce too many false positives
                    Toast.makeText(context,updating + ": " + tmp,Toast.LENGTH_SHORT).show();
                }
            }
            if (updateListAdapter) {
                // Only do the next if we actually did an update from twitter
                if (!fromDbOnly) {
                    Log.i("GTLTask", " scroll to " + result.getNumOriginal());
                    // TODO modify to scroll to last-read position
                    int position = result.getNumOriginal() - 1;
                    if (position>0) {
                        getListView().setSelection(position);
                    }
                }
                else if (unreadCount>1) {
                    getListView().setSelection(unreadCount-1);
                }
            }
            if (progressBar !=null) {
                progressBar.setVisibility(ProgressBar.INVISIBLE);
            }
            if (Build.VERSION.SDK_INT>=11) {
                if (getParent()!=null) {
                    getParent().setProgressBarIndeterminateVisibility(false);
                } else {
                    // A list has no TabWdget as parent
                    setProgressBarIndeterminateVisibility(false);
                }
            }
}
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            if(titleTextBox!=null)
                titleTextBox.setText(updating +" "+ values[0] + "...");
            if (Build.VERSION.SDK_INT>=11) {
                ActionBar ab = getActionBar();
                if (ab!=null)
                    ab.setSubtitle(updating +" "+ values[0] + "...");
            }
        }
    }
    private List<Long> obtainReadIds(List<Status> statusList) {
        List<Long> idsToCheck = new ArrayList<Long>(statusList.size());
        for ( Status status: statusList) {
            idsToCheck.add(status.getId());
        }
        return th.getReadIds(idsToCheck);
    }
    /**
* Set up a filter for tweets. The entries from the filter list are joined together to e.g.
* ".*(http://4sq.com/|http://shz.am/).*"
* @return Error message on issue with pattern, null otherwise
*/
    private String setupFilter() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        String exp = prefs.getString("filter",null);
        if (exp==null) {
            filterPattern = null;
            return null;
        }
        String ret=".*(" + exp.replaceAll(",","|") + ").*";
        Log.i("TweetListActivity::getFilter()","Filter is " + ret);
        try {
            filterPattern = Pattern.compile(ret);
        } catch (PatternSyntaxException e) {
            String tmp = getString(R.string.invalid_filter,e.getLocalizedMessage());
            Log.e("setupFilter",tmp);
            return tmp;
        }
        return null;
    }
}