Android Open Source - MyTwitter-Android Timeline View






From Project

Back to project page MyTwitter-Android.

License

The source code is released under:

Apache License

If you think the Android project MyTwitter-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.fukata.android.mytw;
//from w  w w  .  j  ava 2s. com
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.fukata.android.exandroid.loader.process.BaseRequest;
import org.fukata.android.exandroid.loader.process.ProcessLoader;
import org.fukata.android.exandroid.util.StringUtil;
import org.fukata.android.mytw.TimelineActivity.LoadMode;
import org.fukata.android.mytw.database.dto.TweetDto;
import org.fukata.android.mytw.database.schema.TweetSchema.TweetType;
import org.fukata.android.mytw.util.PrettyDateUtil;
import org.fukata.android.mytw.util.SettingUtil;
import org.fukata.android.mytw.util.StringMatchUtils;
import org.fukata.android.mytw.util.StringUtils;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

public class TimelineView extends ListView implements View.OnClickListener, OnItemLongClickListener {
  // ???????????????????????????????????????????????
  static final int UPDATE_TIMELINE_CHECK_INVERVAL = 10000;

  private static final int NOTIFY_NEW_TWEET = 1;
  
  TimelineActivity parentActivity;
  List<TimelineItem> items;
  TimelineAdapter adapter;

  Button more;
  
  int lastLoadCount;
  String oldestStatusId;      //??????????????????????tweet???statusId
  String latestStatusId;      //???????????????????????tweet???statusId
  long lastUpdateTimeline = System.currentTimeMillis();
  Timer intervalUpdateTimer;

  boolean isFirstLoad = true;
  ItemDialog itemDialog;
  NotificationManager notificationManager;
    
  Handler handler = new Handler();
  
  public TimelineView(Context context, TimelineActivity activity) {
    super(context);
    parentActivity = activity;
    items = new ArrayList<TimelineItem>();
    adapter = newInstanceTimelineAdapter(context, items);
    itemDialog = newInstanceItemDialog();
    notificationManager = (NotificationManager)parentActivity.getSystemService(Context.NOTIFICATION_SERVICE);
    
        View footerView = activity.getLayoutInflater().inflate(R.layout.timeline_footer, null);
        more = (Button) footerView.findViewById(R.id.more);
        more.setOnClickListener(this);
        
        addFooterView(footerView);
        setOnItemLongClickListener(this);
        setAdapter(adapter);

        initTimeline();
        attachUpdateInterval();
  }
  
  void initTimeline() {
    adapter.clear();
    List<TweetDto> tweets = parentActivity.tweetDao.findByType(getTweetType(), SettingUtil.getTimelineCount());
    for (TweetDto tweet : tweets) {
      TimelineItem item = generateTimelineItem(tweet);
      adapter.add(item);
    }
    //????????tweets???????????????statusId??????????
    if (tweets.size() > 0) {
      this.oldestStatusId = tweets.get(tweets.size() - 1).statusId;
      this.latestStatusId = tweets.get(0).statusId;
    }
  }

  TimelineItem generateTimelineItem(TweetDto tweet) {
    TimelineItem item = new TimelineItem();
    
    item.setStatusId(tweet.statusId);
    item.setStatus(tweet.status);
    item.setUsername(tweet.username);
    item.setUserId(tweet.userId);
    item.setSource(tweet.source);
    item.setInReplytoStatusId(tweet.inReplyToStatusId);
    item.setCreatedAt(tweet.createdAt);
    
    return item;
  }

  TweetDto generateTweetDto(TimelineItem item) {
    TweetDto dto = new TweetDto();
    
    dto.statusId = item.getStatusId();
    dto.status = item.getStatus();
    dto.username = item.getUsername();
    dto.userId = item.getUserId();
    dto.source = item.getSource();
    dto.inReplyToStatusId = item.getInReplytoStatusId();
    dto.createdAt = item.getCreatedAt();
    dto.tweetType = getTweetType();
    
    return dto;
  }
  
  ItemDialog newInstanceItemDialog() {
    return new ItemDialog(parentActivity);
  }

  void attachUpdateInterval() {
    intervalUpdateTimer = new Timer();
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        long now = System.currentTimeMillis();
        int interval = SettingUtil.getAutoInterval()*1000;
        if (interval>0 && now>lastUpdateTimeline+interval) {
          // ?????????????????????????????????
          if (parentActivity.timelineLoader.getLoaderQueue().size()==0) {
            loadTimeline(LoadMode.NEW);
          }
        }
      }
    };
    intervalUpdateTimer.schedule(task, UPDATE_TIMELINE_CHECK_INVERVAL, UPDATE_TIMELINE_CHECK_INVERVAL);
  }

  TimelineAdapter newInstanceTimelineAdapter(Context context, List<TimelineItem> items) {
    return new TimelineAdapter(context, items);
  }
  
  TweetType getTweetType() {
    return TweetType.HOME;
  }
  
  void loadTimeline(final LoadMode mode) {
    preLoadTimeline(mode);
    final List<TimelineItem> timeline = new ArrayList<TimelineItem>();
    Runnable successCallback = generateSuccessCallback(timeline, mode);
    processLoadTimeline(timeline, mode, successCallback);
  }
  
  void preLoadTimeline(LoadMode mode) {
    if (LoadMode.REFRESH==mode) {
      more.setEnabled(false);
      more.setText(R.string.retrieving);
    } else if (LoadMode.NEW==mode) {
    } else if (LoadMode.MORE==mode) {
      more.setEnabled(false);
      more.setText(R.string.retrieving);
    }
  }

  void processLoadTimeline(final List<TimelineItem> timeline, final LoadMode mode, Runnable successCallback) {
    parentActivity.timelineLoader.load(new BaseRequest(successCallback, null) {

      @Override
      public void processRequest(ProcessLoader loader) {
        lastLoadCount = 0;
        List<TimelineItem> list = null;
        if (LoadMode.REFRESH==mode) {
          list = getTimeline();
          timeline.addAll(list);
          // 1??????????????????????????????????????
          if (list.size() > 0) {
            refreshCache(timeline);
          }
        } else if (LoadMode.NEW==mode) {
          list = getNewTimeline(latestStatusId);
          lastLoadCount = list.size();
          timeline.addAll(list);
          addCache(list);
        } else if (LoadMode.MORE==mode) {
          list = getMoreTimeline(oldestStatusId);
          timeline.addAll(list);
          addCache(list);
        }
        super.processRequest(loader);
      }
      
      void addCache(final List<TimelineItem> newLoadedItems) {
        Log.d(getClass().getSimpleName(), "addCache");
        List<TweetDto> tweets = new ArrayList<TweetDto>();
        int count = SettingUtil.getTimelineCount();
        int len = timeline.size() > count ? count : timeline.size();
        for (int i=0; i<len; i++) {
          TimelineItem item = timeline.get(i);
          tweets.add( generateTweetDto(item) );
        }
        parentActivity.tweetDao.addTweets(tweets, getTweetType());
      }

      void refreshCache(final List<TimelineItem> timeline) {
        Log.d(getClass().getSimpleName(), "refreshCache");
        List<TweetDto> tweets = new ArrayList<TweetDto>();
        int count = SettingUtil.getTimelineCount();
        int len = timeline.size() > count ? count : timeline.size();
        for (int i=0; i<len; i++) {
          TimelineItem item = timeline.get(i);
          tweets.add( generateTweetDto(item) );
        }
        parentActivity.tweetDao.refreshTweets(tweets, getTweetType());
      }

    });
  }
  
  List<TimelineItem> getMoreTimeline(String lastStatuseId) {
    return parentActivity.twitter.getMoreHomeTimeline(lastStatuseId);
  }

  List<TimelineItem> getNewTimeline(String latestStatuseId) {
    return parentActivity.twitter.getNewHomeTimeline(latestStatuseId);
  }

  List<TimelineItem> getTimeline() {
    return parentActivity.twitter.getHomeTimeline();
  }
  
  Runnable generateSuccessCallback(final List<TimelineItem> timeline, final LoadMode mode) {
    Log.d(getClass().getSimpleName(), "generateSuccessCallback");
    Runnable callback = new Runnable() {
      @Override
      public void run() {
        // ?????????????????????????????
        int firstItemPosition = getFirstVisiblePosition();
        int firstItemTop = getChildCount()==0 || getChildAt(0)==null ? 0 : getChildAt(0).getTop();
        Log.d(getClass().getSimpleName(), "firstItemPosition="+firstItemPosition);
        Log.d(getClass().getSimpleName(), "firstItemTop="+firstItemTop);
        processUpdateTimeline(mode, timeline);
        processFocusItem(mode,firstItemPosition,firstItemTop);
        processNotification(mode);
        lastUpdateTimeline = System.currentTimeMillis();
      };
    };
    return callback;
  }

  void processUpdateTimeline(LoadMode mode, final List<TimelineItem> timeline) {
    more.setText(R.string.more);
    more.setEnabled(true);
    if (timeline.size()>0) {
      if (LoadMode.REFRESH==mode) {
        latestStatusId = timeline.get(0).getStatusId();
        oldestStatusId = timeline.get(timeline.size()-1).getStatusId();
        adapter.clear();
        for (TimelineItem item : timeline) {
          adapter.add(item);
        }
        if (hasWindowFocus()) {
          Toast.makeText(parentActivity.getApplicationContext(), R.string.update_successful, Toast.LENGTH_LONG).show();
        }
      } else if (LoadMode.NEW==mode) {
        int insertAt = 0;
        for (TimelineItem item : timeline) {
          if (!StringUtil.equals(latestStatusId, item.getStatusId())) {
            adapter.insert(item, insertAt);
            insertAt++;
          }
        }
        latestStatusId = timeline.get(0).getStatusId();
      } else if (LoadMode.MORE==mode) {
        for (TimelineItem item : timeline) {
          if (!StringUtil.equals(oldestStatusId, item.getStatusId())) {
            adapter.add(item);
          }
        }
        oldestStatusId = timeline.get(timeline.size()-1).getStatusId();
        if (hasWindowFocus()) {
          Toast.makeText(parentActivity.getApplicationContext(), R.string.update_successful, Toast.LENGTH_LONG).show();
        }
      }
    }
  }
  
  void processNotification(LoadMode mode) {
    if (hasWindowFocus() || !SettingUtil.isNotificationEnabled()) {
      return;
    }
    
    if (mode == LoadMode.NEW && lastLoadCount>0) {
      notificationNewTweet();
    }
  }
  
  void notificationNewTweet() {
    String ticker = parentActivity.getString(R.string.notify_new_tweet, lastLoadCount);
    Intent intent = new Intent(parentActivity, TimelineActivity.class);
    intent.setAction(Intent.ACTION_VIEW);
    PendingIntent contentIntent = PendingIntent.getActivity(parentActivity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    Notification notification = new Notification(android.R.drawable.ic_menu_add, ticker, System.currentTimeMillis());
    notification.setLatestEventInfo(parentActivity.getApplicationContext(), parentActivity.getString(R.string.app_name), ticker, contentIntent);
    notification.flags = Notification.FLAG_AUTO_CANCEL;
    notificationManager.notify(NOTIFY_NEW_TWEET, notification);
  }

  void processFocusItem(LoadMode mode, int firstItemPosition, int firstItemTop) {
    if (mode == LoadMode.NEW) {
      //??????????????????????
      setSelectionFromTop(firstItemPosition+lastLoadCount, firstItemTop);
    }
  }

  void doResume() {
    Log.d(getClass().getSimpleName(), "doResume");
    boolean noCachedTimeline = this.latestStatusId == null; //???????????????????true???????
    if (isFirstLoad && noCachedTimeline) {
      loadTimeline(LoadMode.REFRESH);
    } else {
      loadTimeline(LoadMode.NEW);
    }
    isFirstLoad = false;
  }
  
  @Override
  public void onClick(View v) {
    if (v.getId()==R.id.more) {
      loadTimeline(LoadMode.MORE);
    }
  }
  
  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
    if (items.size()<=position) {
      return false;
    }

    TimelineItem item = items.get(position);
    if (item==null) {
      return false;
    }

    itemDialog.show(item);
    return false;
  }
  
  @Override
    public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP && this.getScrollY() < -60) {
      // ?????????????????????????????
      loadTimeline(LoadMode.REFRESH);
    }
    return super.onTouchEvent(event);
    }
  
//  CharSequence getTitle() {
//    return parentActivity.getString(R.string.title_home);
//  }
  
  void postRetweet(final TimelineItem item) {
    Runnable successCallback = new Runnable() {
      @Override
      public void run() {
        Toast.makeText(parentActivity.getApplicationContext(), R.string.retweet_successful, Toast.LENGTH_LONG).show();
        loadTimeline(LoadMode.NEW);
      }
    };
    parentActivity.timelineLoader.load(new BaseRequest(successCallback, null) {
      @Override
      public void processRequest(ProcessLoader loader) {
        parentActivity.twitter.postReTweet(item.getStatusId());
        super.processRequest(loader);
      }
    });
  }
  
  void postFavorites(final TimelineItem item) {
    Runnable successCallback = new Runnable() {
      @Override
      public void run() {
        Toast.makeText(parentActivity.getApplicationContext(), R.string.favorites_successful, Toast.LENGTH_LONG).show();
      }
    };
    parentActivity.timelineLoader.load(new BaseRequest(successCallback, null) {
      @Override
      public void processRequest(ProcessLoader loader) {
        parentActivity.twitter.postFavorites(item.getStatusId());
        super.processRequest(loader);
      }
    });
  }

  void deleteTweet(final TimelineItem item) {
    //FIXME ???
  }
  
  class ItemDialog extends AlertDialog.Builder {
    ListView optionsView;
    ArrayAdapter<String> optionsAdapter;
    Activity activity;

    public ItemDialog(Activity activity) {
      super(activity);
      this.activity = activity;
      setTitle(R.string.options);
    }
    
    public void show(final TimelineItem item) {
      final TimelineMenu menu = new TimelineMenu(item, parentActivity);
      String[] options = menu.getMenuOptions();
      
      setItems(options, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int index) {
          TimelineMenuOption which = menu.getAction(index);
          if (TimelineMenuOption.RETWEET == which) {
            postRetweet(item);
          } else if (TimelineMenuOption.RETWEET_WITH_COMMENT==which) {
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setClass(activity, UpdateStatusActivity.class);
            intent.putExtra(Intent.EXTRA_TEXT, " RT @"+item.getUsername()+": "+item.getStatus());
            intent.putExtra(MyTwitterApp.INTENT_EXTRA_SELECTION, MyTwitterApp.INTENT_EXTRA_SELECTION_HEAD);
            parentActivity.startActivity(intent);
          } else if (TimelineMenuOption.REPLY==which) {
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setClass(activity, UpdateStatusActivity.class);
            intent.putExtra(Intent.EXTRA_TEXT, "@"+item.getUsername()+" ");
            intent.putExtra(MyTwitterApp.INTENT_EXTRA_SELECTION, MyTwitterApp.INTENT_EXTRA_SELECTION_END);
            parentActivity.startActivity(intent);
          } else if (TimelineMenuOption.FAVORITES == which) {
            postFavorites(item);
          } else if (TimelineMenuOption.URLS == which) {
            final List<String> urls = StringMatchUtils.getUrls(item.getStatus());
            if (urls.size() == 1) {
              //URL????????????????????????????????????????????
              openOnExternalWebBrowser(urls.get(0));
            } else if (urls.size() > 1) {
              //URL??????????????????????????????????
              String[] urlMenus = new String[urls.size()];
              for (int i = 0; i < urls.size(); i++) {
                urlMenus[i] = parentActivity.getString(R.string.jump_to, StringUtils.strimwidth(urls.get(i), 40, "..."));
              }
              setItems(urlMenus, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                  openOnExternalWebBrowser(urls.get(which));
                }
              });
              create().show();
            }
          } else if (TimelineMenuOption.MENTIONED_FOR == which) {
            (new Thread(new Runnable() {
              @Override
              public void run() {
                final TimelineItem mentionFor = parentActivity.twitter.show(item.getInReplytoStatusId());

                handler.post(new Runnable() {
                  @Override
                  public void run() {
                    if (mentionFor != null) {
                      AlertDialog.Builder bld = new AlertDialog.Builder(parentActivity);
                      bld.setTitle(mentionFor.getUsername() + " " + PrettyDateUtil.toString(mentionFor.getCreatedAt()));
                      bld.setMessage(mentionFor.getStatus());
                      bld.show();
                    } else {
                      Toast.makeText(
                          parentActivity.getApplicationContext(), 
                          "ERROR: failed to retreive a mention. " + item.getInReplytoStatusId(), 
                          Toast.LENGTH_LONG).show();
                    }
                  }
                });              
              }
            })).start();
          }
        }

        /**
         * ?????????URL??????
         */
        private void openOnExternalWebBrowser(String url) {
          try {
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            parentActivity.startActivity(intent);
          } catch (Exception e) {
            Toast.makeText(parentActivity.getApplicationContext(), R.string.update_unsuccessful, Toast.LENGTH_LONG).show();
          }
        }
      });
      create().show();
    }
  }
  
  /**
   * ????????????????????????????????
   */
  enum TimelineMenuOption {
    RETWEET(R.string.retweet),
    RETWEET_WITH_COMMENT(R.string.retweet_with_comment),
    REPLY(R.string.reply),
    FAVORITES(R.string.favorites),
    URLS(R.string.urls),
    MENTIONED_FOR(R.string.mentioned_for);
    
    private int resourceId;
    private TimelineMenuOption(int resourceId) {
      this.resourceId = resourceId;
    }
    public int getResourceId() {
      return this.resourceId;
    }
  }
  
  /**
   * ???????????????
   */
  class TimelineMenu {
    private List<TimelineMenuOption> options = new ArrayList<TimelineMenuOption>();
    private TimelineActivity parentActivity;
    
    public TimelineMenu(TimelineItem item, TimelineActivity parentActivity) {
      this.parentActivity = parentActivity;
      //compose menu item.
      options.add(TimelineMenuOption.RETWEET);
      options.add(TimelineMenuOption.RETWEET_WITH_COMMENT);
      options.add(TimelineMenuOption.REPLY);
      options.add(TimelineMenuOption.FAVORITES);

      List<String> urls = StringMatchUtils.getUrls(item.getStatus());
      if (urls.size() > 0) {
        options.add(TimelineMenuOption.URLS);
      }
      String inReplyToStatusId = item.getInReplytoStatusId();
      Log.d(getClass().getSimpleName(), "inreplytostatusId:" + inReplyToStatusId);
      if (inReplyToStatusId != null && inReplyToStatusId.length() > 0) {
        options.add(TimelineMenuOption.MENTIONED_FOR);
      }
    }
    public String[] getMenuOptions() {
      List<String> options = new ArrayList<String>();
      for (TimelineMenuOption o : this.options) {
        options.add(this.parentActivity.getString(o.getResourceId()));
      }
      return options.toArray(new String[0]);
    }
    public TimelineMenuOption getAction(int index) {
      return this.options.get(index);
    }
  }
  
}




Java Source Code List

org.fukata.android.mytw.DirectMessageTimelineAdapter.java
org.fukata.android.mytw.DirectMessageTimelineView.java
org.fukata.android.mytw.HomeTimelineView.java
org.fukata.android.mytw.MentionTimelineView.java
org.fukata.android.mytw.MyTwitterActivity.java
org.fukata.android.mytw.MyTwitterApp.java
org.fukata.android.mytw.SettingsActivity.java
org.fukata.android.mytw.TimelineActivity.java
org.fukata.android.mytw.TimelineAdapter.java
org.fukata.android.mytw.TimelineItem.java
org.fukata.android.mytw.TimelineView.java
org.fukata.android.mytw.UpdateStatusActivity.java
org.fukata.android.mytw.database.MyTwitterDB.java
org.fukata.android.mytw.database.dao.BaseDao.java
org.fukata.android.mytw.database.dao.TweetDao.java
org.fukata.android.mytw.database.dto.TweetDto.java
org.fukata.android.mytw.database.schema.TweetSchema.java
org.fukata.android.mytw.receiver.InstallAnalyticsReceiver.java
org.fukata.android.mytw.twitter.Twitter.java
org.fukata.android.mytw.twitter.rs.Status.java
org.fukata.android.mytw.twitter.rs.User.java
org.fukata.android.mytw.util.PrettyDateUtil.java
org.fukata.android.mytw.util.SettingUtil.java
org.fukata.android.mytw.util.StringMatchUtils.java
org.fukata.android.mytw.util.StringUtils.java