package com.thankcreate.care.status; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.buuuk.android.gallery.ImageViewFlipper; import com.handmark.pulltorefresh.library.PullToRefreshBase; import com.handmark.pulltorefresh.library.PullToRefreshBase.OnLastItemVisibleListener; import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener; import com.handmark.pulltorefresh.library.PullToRefreshListView; import com.markupartist.android.widget.ActionBar; import com.markupartist.android.widget.ActionBar.Action; import com.thankcreate.care.App; import com.thankcreate.care.AppConstants; import com.thankcreate.care.BaseActivity; import com.thankcreate.care.R; import com.thankcreate.care.R.drawable; import com.thankcreate.care.R.layout; import com.thankcreate.care.R.menu; import com.thankcreate.care.account.AccountActivity; import com.thankcreate.care.account.AccountSelectFreindActivity; import com.thankcreate.care.rss.RssDetailActivity; import com.thankcreate.care.tool.misc.DateTool; import com.thankcreate.care.tool.misc.FirstCharactorComparator; import com.thankcreate.care.tool.misc.MiscTool; import com.thankcreate.care.tool.misc.PreferenceHelper; import com.thankcreate.care.tool.misc.StringTool; import com.thankcreate.care.tool.ui.DrawableManager; import com.thankcreate.care.tool.ui.RefreshViewerHelper; import com.thankcreate.care.tool.ui.ToastHelper; import com.thankcreate.care.tool.ui.RefreshViewerHelper.OnRefreshCompleteListener; import com.thankcreate.care.viewmodel.EntryType; import com.thankcreate.care.viewmodel.FriendViewModel; import com.thankcreate.care.viewmodel.ItemViewModel; import com.thankcreate.care.viewmodel.SimpleTableModel; import com.umeng.analytics.MobclickAgent; import com.umeng.update.UmengDownloadListener; import com.umeng.update.UmengUpdateAgent; import android.os.Bundle; import android.os.DropBoxManager.Entry; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.graphics.Rect; import android.text.format.DateUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView.ScaleType; import android.widget.AdapterView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; /** * 这个类必须是在tabhost里第一个显示的类 因为一些初始化工作是在这个类里做的 如果以后想把第一个显示页改成其它的 需要考虑这些初始化工作怎么适当处理 * * @author ThankCreate */ public class StatusTimelineActivity extends BaseActivity implements OnRefreshCompleteListener { private ActionBar actionBar; private RefreshViewerHelper refreshViewerHelper; private DrawableManager drawableManager = App.getDrawableManager(); private StatusTimelineAdapter adapter; private ListView listViewTimeline; private PullToRefreshListView pullToRefreshListView; private int sourceSelected = 0; private int[] typeArray = { EntryType.SinaWeibo, EntryType.Renren, EntryType.Douban }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_status_timeline); initControl(); initActionBar(); initSourceSelected(); checkNetWork(); initAutoUpdate(); // 有米初始化 ,不加广告了 // AdManager.init(this.getBaseContext(), "3e9fc4796d5e9801", // "4d78213f8ac82754 ", 45, false); } private void initAutoUpdate() { // 做友盟自动更新,因为这里是在tabhost中,所以要getParent // 据说是因为tabhost的子页面拿不到service UmengUpdateAgent.setOnDownloadListener(new UmengDownloadListener() { @Override public void OnDownloadEnd(int result) { if(result == 0) { ToastHelper.show("自动更新失败,请确保网络连接通畅", true); } } }); UmengUpdateAgent.update(this.getParent()); } private void checkNetWork() { if (!MiscTool.isOnline()) { ToastHelper.show("当前网络连接不可用", true); } } private void initControl() { pullToRefreshListView = (PullToRefreshListView) findViewById(R.id.listViewTimeline); listViewTimeline = pullToRefreshListView.getRefreshableView(); pullToRefreshListView .setOnRefreshListener(new OnRefreshListener<ListView>() { @Override public void onRefresh( PullToRefreshBase<ListView> refreshView) { refresh(); } }); SharedPreferences pref = App.getAppContext().getSharedPreferences( AppConstants.PREFERENCES_NAME, Context.MODE_APPEND); Long lastTime = pref.getLong("Global_LastUpdateTime", 0); String label = ""; if(lastTime == 0) { label = "从未更新"; } else { label = "上次更新: " + DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); } pullToRefreshListView.getLoadingLayoutProxy() .setLastUpdatedLabel(label); pullToRefreshListView.setOnItemClickListener(mOnItemClickListener); pullToRefreshListView.setOnLastItemVisibleListener(mOnLastItemVisibleListener); } private void initActionBar() { actionBar = (ActionBar) findViewById(R.id.actionbar); actionBar.setTitle("我只在乎你"); actionBar.addActionLeft(new Action() { @Override public void performAction(View view) { postStatusClicked(); } @Override public int getDrawable() { return drawable.thumb_write_new; } }); actionBar.addActionRight(new Action() { @Override public void performAction(View view) { refresh(); } @Override public int getDrawable() { return drawable.thumb_refresh; } }); } private void initSourceSelected() { int tp = MiscTool.getFirstFoundLoginType(); for (int i = 0; i < typeArray.length; i++) { if (typeArray[i] == tp) sourceSelected = i; } } private void postStatusClicked() { new AlertDialog.Builder(this) .setIcon(R.drawable.thumb_write_new) .setTitle("选择发布源") .setSingleChoiceItems(R.array.array_post_source_choose, sourceSelected, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { sourceSelected = whichButton; } }) .setPositiveButton(R.string.dialog_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (sourceSelected == 0) postStatusSinaWeibo(); else if (sourceSelected == 1) postStatusRenren(); else if (sourceSelected == 2) postStatusDouban(); } }) .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { Log.i("btnIndex", String.valueOf(whichButton)); } }).create().show(); } private void postStatusSinaWeibo() { if (!MiscTool.isAuthValid(EntryType.SinaWeibo)) { ToastHelper.show("新浪微博尚未登陆,或者登陆已过期~"); return; } gotoStatusPostPage(EntryType.SinaWeibo); } private void postStatusRenren() { if (!MiscTool.isAuthValid(EntryType.Renren)) { ToastHelper.show("人人帐号尚未登陆,或者登陆已过期~"); return; } gotoStatusPostPage(EntryType.Renren); } private void postStatusDouban() { if (!MiscTool.isAuthValid(EntryType.Douban)) { ToastHelper.show("豆瓣帐号尚未登陆,或者登陆已过期~"); return; } gotoStatusPostPage(EntryType.Douban); } private void gotoStatusPostPage(int type) { Intent intent = new Intent(); intent.setClass(this, StatusPostActivity.class); intent.putExtra("type", type); startActivity(intent); } private OnItemClickListener mOnItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long id) { // 这里的position是从1开始算起的,真奇怪,应该是pull to refresh的header占住0了 --position; if (position < 0 || position >= adapter.listModel.size()) return; ItemViewModel item = adapter.listModel.get(position); Intent intent = new Intent(); if (item.type == EntryType.Rss) { intent.setClass(StatusTimelineActivity.this, RssDetailActivity.class); intent.putExtra("itemViewModel", item); startActivity(intent); } else if (item.type == EntryType.SinaWeibo || item.type == EntryType.Renren || item.type == EntryType.Douban) { intent.setClass(StatusTimelineActivity.this, StatusDetailActivity.class); intent.putExtra("itemViewModel", item); startActivity(intent); } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_status_timeline, menu); return false; } /** * Android版的“我只在乎你”的缓存机制与其它3个版本不一样 * 其它三个版本都是把缓存直接加载到mainViewModel的item里,然后再把网络得到的数据加载到同样的位置 * 有一个明显的先后顺序,缓存的加载必然比从网络得到的快 但是因为安卓的Splash是个伪启动画面,它就是一个普通的Activity加上延时跳转 * 为了不浪费那个延时,在延时阶段就开始做预加载 所以导致当Timeline页面开始显示时,有可能缓存和网络数据都在写item * 为了解决这个问题,把缓存数据直接存在这StatusTimeline这个Activity里 如果满足 refreshViewerHelper.isComplete = false * 说明这个时候网络返回还没做完,那么就开始加载缓存 */ @Override protected void onResume() { super.onResume(); if (refreshViewerHelper == null) { refreshViewerHelper = RefreshViewerHelper.getAppInstance(); refreshViewerHelper.addListenter(this); // 因为在Splash页面时就已经在做预加载,所以到这里时,可能还在loading,也可能已经做完了loading if (refreshViewerHelper.isLoading) actionBar.setProgressBarVisibility(View.VISIBLE); // 如果预加载已经做完,则使用mainViewModel用的数据做刷新 if (refreshViewerHelper.isComplete) onRefreshComplete(); // 如果预加载还没做完,则使用本地缓存做第一次加载 else { refreshFromCache(); } } if (App.mainViewModel.isChanged) { refresh(); } else { if (App.memoryCleaned) { App.memoryCleaned = false; onRefreshComplete(); } } if(adapter != null) adapter.refresh(); } private void refresh() { actionBar.setProgressBarVisibility(View.VISIBLE); refreshViewerHelper.refreshMainViewModel(); } /** * onRefreshComplete 和 refreshFromCache要加锁 * 理论上大部分时候应该是先refreshFromCache再会onRefreshComplete 但是可能同时进入,所以加个锁保险点 */ @Override public synchronized void onRefreshComplete() { pullToRefreshListView.post(new Runnable() { @Override public void run() { Long current = System.currentTimeMillis(); String label = "上次更新: " + DateUtils.formatDateTime(getApplicationContext(), current, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); pullToRefreshListView.getLoadingLayoutProxy() .setLastUpdatedLabel(label); SharedPreferences pref = App.getAppContext() .getSharedPreferences(AppConstants.PREFERENCES_NAME, Context.MODE_APPEND); Editor editor = pref.edit(); editor.putLong("Global_LastUpdateTime", current); editor.commit(); } }); adapter = new StatusTimelineAdapter(this); final List<ItemViewModel> input; if (App.mainViewModel.items.size() == 0) { input = new ArrayList<ItemViewModel>(); ItemViewModel model = generateNoteItemViewModel(); model.type = EntryType.NotSet; input.add(model); } else { input = App.mainViewModel.items; } adapter.setListModel(input); listViewTimeline.post(new Runnable() { @Override public void run() { actionBar.setProgressBarVisibility(View.GONE); pullToRefreshListView.onRefreshComplete(); listViewTimeline.setAdapter(adapter); } }); } public synchronized void refreshFromCache() { // 读缓存 try { File myDir = App.getAppContext().getFilesDir(); File cacheFile = new File(myDir, AppConstants.CACHE_ITEM); FileInputStream fis = new FileInputStream(cacheFile); ObjectInputStream ois = new ObjectInputStream(fis); ArrayList<ItemViewModel> cacheItems = (ArrayList<ItemViewModel>) ois .readObject(); adapter = new StatusTimelineAdapter(this); if (cacheItems.size() == 0) { ItemViewModel model = generateNoteItemViewModel(); model.type = EntryType.NotSet; cacheItems.add(model); } adapter.setListModel(cacheItems); listViewTimeline.post(new Runnable() { @Override public void run() { listViewTimeline.setAdapter(adapter); } }); ois.close(); } catch (Exception e) { e.printStackTrace(); } } private ItemViewModel generateNoteItemViewModel() { ItemViewModel model = new ItemViewModel(); model.type = EntryType.NotSet; model.title = ">_<"; model.content = "未得到任何信息,请先设置好关注人,并确保网络畅通~"; return model; } private OnLastItemVisibleListener mOnLastItemVisibleListener = new OnLastItemVisibleListener() { @Override public void onLastItemVisible() { adapter.showNext(); } }; class StatusTimelineAdapter extends BaseAdapter { public List<ItemViewModel> listModel = new ArrayList(); private LayoutInflater mInflater; private final int SHOW_FIRST_TIME = 30; private final int SHOW_EACH_TIME = 20; private int currentShowCount; public StatusTimelineAdapter(Context context) { super(); mInflater = LayoutInflater.from(context); currentShowCount = SHOW_FIRST_TIME; } public void addItem(ItemViewModel model) { listModel.add(model); notifyDataSetChanged(); } public void setListModel(List<ItemViewModel> input) { listModel = input; notifyDataSetChanged(); } public void refresh() { notifyDataSetChanged(); } /** * 滑动到底时,加载更多 * @return true:有更多项可供加载 false:当前adapter的listmodel已经全部加载了 */ public boolean showNext() { if (currentShowCount < listModel.size()) { currentShowCount += SHOW_EACH_TIME; notifyDataSetChanged(); return true; } else return false; } @Override public int getCount() { if(listModel.size() < currentShowCount) return listModel.size(); return currentShowCount; } @Override public Object getItem(int position) { try { return listModel.get(position); } catch (Exception e) { return null; } } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate( R.layout.listview_item_status_timeline, null); holder.imageAvatar = (ImageView) convertView .findViewById(R.id.status_list_item_avatar); holder.textTitle = (TextView) convertView .findViewById(R.id.status_list_item_title); holder.textContent = (TextView) convertView .findViewById(R.id.status_list_item_content); holder.imageThumb = (ImageView) convertView .findViewById(R.id.status_list_item_thumb); holder.layoutForward = (LinearLayout) convertView .findViewById(R.id.status_list_item_forward); holder.textForwardContent = (TextView) convertView .findViewById(R.id.status_list_item_forward_content); holder.imageForwardThumb = (ImageView) convertView .findViewById(R.id.status_list_item_forward_thumb); holder.textTime = (TextView) convertView .findViewById(R.id.status_list_item_time); holder.textCommentCount = (TextView) convertView .findViewById(R.id.status_list_item_comment_count); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final ItemViewModel item = listModel.get(position); if (item == null) return null; holder.imageAvatar.setTag(item.iconURL); if (item.type == EntryType.Rss) { holder.imageAvatar.setImageResource(R.drawable.thumb_rss); } else if (item.type == EntryType.NotSet) { holder.imageAvatar.setImageResource(R.drawable.thumb_cry); } else { drawableManager.fetchDrawableOnThread(item.iconURL, holder.imageAvatar); } holder.textTitle.setText(item.title); holder.textContent.setText(item.content); if (StringTool.isNullOrEmpty(item.content)) { holder.textContent.setVisibility(View.GONE); } else { holder.textContent.setVisibility(View.VISIBLE); } if (StringTool.isNullOrEmpty(item.imageURL)) { holder.imageThumb.setVisibility(View.GONE); } else { holder.imageThumb.setVisibility(View.VISIBLE); holder.imageThumb.setTag(item.imageURL); holder.imageThumb.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(StatusTimelineActivity.this, ImageViewFlipper.class); intent.putExtra("src", item.fullImageURL); startActivity(intent); } }); // 这里必须提前做大小的缓存,因为如果是从下往上滑的话,由于size是异步返回的 // 会有一次明显的卡顿 Rect cacheSize = drawableManager.adjustSizeMap .get(item.imageURL); if (cacheSize != null) { LayoutParams params = holder.imageThumb.getLayoutParams(); params.height = cacheSize.height(); params.width = cacheSize.width(); holder.imageThumb.setLayoutParams(params); } DisplayMetrics metric = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metric); int maxSize = (int) (80 * metric.density); drawableManager.fetchDrawableOnThread(item.imageURL, holder.imageThumb, maxSize, true, this); } // 转发部分 if (item.forwardItem == null) { holder.layoutForward.setVisibility(View.GONE); } else { holder.layoutForward.setVisibility(View.VISIBLE); holder.textForwardContent.setText(item.forwardItem .getContentWithTitle()); if (StringTool.isNullOrEmpty(item.forwardItem .getContentWithTitle())) { holder.textForwardContent.setVisibility(View.GONE); } else { holder.textForwardContent.setVisibility(View.VISIBLE); } if (StringTool.isNullOrEmpty(item.forwardItem.imageURL)) { holder.imageForwardThumb.setVisibility(View.GONE); } else { holder.imageForwardThumb.setVisibility(View.VISIBLE); holder.imageForwardThumb.setTag(item.forwardItem.imageURL); holder.imageForwardThumb .setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass( StatusTimelineActivity.this, ImageViewFlipper.class); intent.putExtra("src", item.forwardItem.fullImageURL); startActivity(intent); } }); Rect cacheSize = drawableManager.adjustSizeMap .get(item.forwardItem.imageURL); if (cacheSize != null) { LayoutParams params = holder.imageForwardThumb .getLayoutParams(); params.height = cacheSize.height(); params.width = cacheSize.width(); holder.imageForwardThumb.setLayoutParams(params); } DisplayMetrics metric = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metric); int maxSize = (int) (80 * metric.density); drawableManager.fetchDrawableOnThread( item.forwardItem.imageURL, holder.imageForwardThumb, maxSize, true, this); } } holder.textTime.setText(DateTool .convertDateToStringInShow(item.time)); holder.textCommentCount.setText(item.getCommentCount()); return convertView; } public class ViewHolder { public ImageView imageAvatar; public TextView textTitle; public TextView textContent; public ImageView imageThumb; public LinearLayout layoutForward; public TextView textForwardContent; public ImageView imageForwardThumb; public TextView textTime; public TextView textCommentCount; public int tag; } } }