package com.thankcreate.care.tool.ui;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mcsoxford.rss.RSSFeed;
import org.mcsoxford.rss.RSSItem;
import org.mcsoxford.rss.RSSReader;
import android.R.bool;
import android.R.integer;
import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.JsonReader;
import android.util.Log;
import com.dongxuexidu.douban4j.constants.DefaultConfigs;
import com.dongxuexidu.douban4j.model.app.AccessToken;
import com.dongxuexidu.douban4j.provider.OAuthDoubanProvider;
import com.dongxuexidu.douban4j.utils.HttpManager;
import com.hp.hpl.sparta.xpath.ThisNodeTest;
import com.renren.api.connect.android.AsyncRenren;
import com.renren.api.connect.android.exception.RenrenError;
import com.thankcreate.care.App;
import com.thankcreate.care.AppConstants;
import com.thankcreate.care.account.AccountActivity;
import com.thankcreate.care.service.NewsPollingService;
import com.thankcreate.care.tool.converter.DoubanConverter;
import com.thankcreate.care.tool.converter.RenrenConverter;
import com.thankcreate.care.tool.converter.RssConverter;
import com.thankcreate.care.tool.converter.SinaWeiboConverter;
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.misc.TaskHelper;
import com.thankcreate.care.tool.misc.TaskHelper.OnTaskCompleteListener;
import com.thankcreate.care.viewmodel.FriendViewModel;
import com.thankcreate.care.viewmodel.ItemViewModel;
import com.thankcreate.care.viewmodel.MainViewModel;
import com.thankcreate.care.viewmodel.PictureItemViewModel;
import com.weibo.sdk.android.Oauth2AccessToken;
import com.weibo.sdk.android.WeiboException;
import com.weibo.sdk.android.api.StatusesAPI;
import com.weibo.sdk.android.api.WeiboAPI.FEATURE;
import com.weibo.sdk.android.net.RequestListener;
/**
* 由于安卓版“我只在乎你”可以实现推送机制,这里的RefreshViewerHelper分为两类
* 一类是供前台程序运行的RefreshViewerHelper,它直接与App的MainViewModel绑定
* 另一类是供后台Service运行的RefreshViewerHelper,它刷新的是后台Service的那个临时MainViewModel
* 今后,安卓版所有的刷新操作,都不能直接拿App的MainViewModel,而是要一步步传参传过去
* 注意:此类中所有的涉及UI的操作,都应该判断mType的状态,只有前台RefreshViewerHelper才能触发Toast
* 注意:后台轮询时要最大化节省用户流量
* @author ThankCreate
*/
public class RefreshViewerHelper implements OnTaskCompleteListener {
private static final int FORGRAOUND = 1;
private static final int BACKGROUND = 2;
private int mType;
private TaskHelper taskHelper;
public boolean isLoading = false;
public boolean isComplete = false; // isComplete本质上来说,只是用来判断预加载那一次有没有做完
private MainViewModel mainViewModel;
private List<OnRefreshCompleteListener> listListeners = new ArrayList<RefreshViewerHelper.OnRefreshCompleteListener>();
private static RefreshViewerHelper sAppRefreshViewerHelper = null;
private static RefreshViewerHelper sServiceRefreshViewerHelper = null;
public static RefreshViewerHelper getAppInstance() {
if (sAppRefreshViewerHelper == null) {
sAppRefreshViewerHelper = new RefreshViewerHelper();
sAppRefreshViewerHelper.mType = FORGRAOUND;
sAppRefreshViewerHelper.mainViewModel = App.mainViewModel;
}
return sAppRefreshViewerHelper;
}
public static RefreshViewerHelper getServiceInstance(
MainViewModel mainViewModel) {
if (sServiceRefreshViewerHelper == null) {
sServiceRefreshViewerHelper = new RefreshViewerHelper();
sServiceRefreshViewerHelper.mType = BACKGROUND;
}
sServiceRefreshViewerHelper.mainViewModel = mainViewModel;
return sServiceRefreshViewerHelper;
}
private RefreshViewerHelper() {
super();
taskHelper = new TaskHelper(this);
}
public void addListenter(OnRefreshCompleteListener listener) {
listListeners.add(listener);
}
public void removeListenter(OnRefreshCompleteListener listener) {
listListeners.remove(listener);
}
public void refreshMainViewModel() {
if (!MiscTool.isOnline()) {
notifyListeners();
return;
}
if (isLoading)
return;
isLoading = true;
mainViewModel.isChanged = false;
taskHelper.clear();
taskHelper.pushTask("SinaWeibo");
taskHelper.pushTask("Renren");
taskHelper.pushTask("Douban");
taskHelper.pushTask("Rss");
// 1.Weibo
refreshModelSinaWeibo();
// 2.Rss
refreshModelRssFeed();
// 3.Renren
refreshModelRenren();
// 4.Douban
refreshModelDouban();
}
private void refreshModelSinaWeibo() {
mainViewModel.sinaWeiboItems.clear();
mainViewModel.sinaWeiboPictureItems.clear();
// 1.判断是否登陆
Boolean isLoggin = MiscTool.isSinaWeiboLogin();
if (!isLoggin) {
taskHelper.popTask("SinaWeibo");
return;
}
// 2.判断是否关注了用户
String strFollowerID = PreferenceHelper
.getString("SinaWeibo_FollowerID");
if (StringTool.isNullOrEmpty(strFollowerID)) {
taskHelper.popTask("SinaWeibo");
return;
}
// 3.判断是否过期
long exp = PreferenceHelper.getLong("SinaWeibo_ExpirationDate");
if (exp < System.currentTimeMillis()) {
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 新浪微博授权已过期,请到帐号页重新登陆以授权");
PreferenceHelper.removeSinaWeiboPreference();
}
taskHelper.popTask("SinaWeibo");
return;
}
Oauth2AccessToken oa = MiscTool.getOauth2AccessToken();
if (oa == null)
return;
StatusesAPI statusesAPI = new StatusesAPI(oa);
// 新浪微博最多一次加载100
int count = 80;
if(mType == FORGRAOUND)
count = 80;
else
count = 5; // 后台轮询时,要最大化节约用户流量
statusesAPI.userTimeline(Long.parseLong(strFollowerID), 0, 0, count, 1,
false, FEATURE.ALL, false, mSinaWeiboUserTimeLineListener);
}
private RequestListener mSinaWeiboUserTimeLineListener = new RequestListener() {
@Override
public void onComplete(String arg0) {
try {
JSONObject root = new JSONObject(arg0);
JSONArray listStatus = root.optJSONArray("statuses");
if (listStatus == null || listStatus.length() == 0)
return;
for (int i = 0; i < listStatus.length(); i++) {
JSONObject status = listStatus.getJSONObject(i);
ItemViewModel model = SinaWeiboConverter
.convertStatusToCommon(status, mainViewModel);
if (model != null) {
mainViewModel.sinaWeiboItems.add(model);
}
}
} catch (Exception e) {
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 新浪微博信息获取发生未知错误");
}
e.printStackTrace();
} finally {
taskHelper.popTask("SinaWeibo");
}
}
@Override
public void onIOException(IOException arg0) {
taskHelper.popTask("SinaWeibo");
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 新浪微博信息获取发生未知错误");
}
}
@Override
public void onError(WeiboException arg0) {
taskHelper.popTask("SinaWeibo");
if (mType == FORGRAOUND) {
if (arg0.getStatusCode() == 21327) {
ToastHelper.show(">_< 新浪微博授权已过期,请到帐号页重新登陆以授权");
} else {
ToastHelper.show(">_< 新浪微博信息获取发生未知错误");
}
}
}
};
private void refreshModelRssFeed() {
mainViewModel.rssItems.clear();
final String url = PreferenceHelper.getString("RSS_FollowerPath");
if (StringTool.isNullOrEmpty(url)) {
taskHelper.popTask("Rss");
return;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
RSSReader rssReader = new RSSReader();
RSSFeed rssFeed = rssReader.load(url);
if (rssFeed.getItems() != null
&& rssFeed.getItems().size() != 0) {
for (int i = 0; i < rssFeed.getItems().size(); i++) {
RSSItem item = rssFeed.getItems().get(i);
ItemViewModel model = RssConverter
.convertStatusToCommon(item);
if (model != null)
mainViewModel.rssItems.add(model);
}
}
taskHelper.popTask();
} catch (Exception e) {
if (mType == FORGRAOUND) {
ToastHelper.show(">_< RSS订阅信息获取发生未知错误");
}
taskHelper.popTask("Rss");
}
}
}).start();
}
private void refreshModelRenren() {
mainViewModel.renrenItems.clear();
mainViewModel.renrenPictureItems.clear();
// 1.判断是否登陆
Boolean isLoggin = MiscTool.isRenrenLogin();
if (!isLoggin) {
taskHelper.popTask("Renren");
return;
}
// 2.判断是否关注了用户
String strFollowerID = PreferenceHelper.getString("Renren_FollowerID");
if (StringTool.isNullOrEmpty(strFollowerID)) {
taskHelper.popTask("Renren");
return;
}
// 3.判断是否过期
long exp = PreferenceHelper.getLong("Renren_ExpirationDate");
if (exp < System.currentTimeMillis()) {
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 人人帐号授权已过期,请到帐号页重新登陆以授权");
PreferenceHelper.removeRenrenPreference();
}
taskHelper.popTask("Renren");
return;
}
AsyncRenren asyncRenren = new AsyncRenren(App.getRenren());
Bundle bd = new Bundle();
bd.putString("method", "feed.get");
bd.putString("type", "10,30,32");
bd.putString("uid", strFollowerID);
if(mType == FORGRAOUND)
bd.putString("count", "50"); // 人人最多就为50
else
bd.putString("count", "10"); // 后台运行的话,最大化节省流量
asyncRenren.requestJSON(bd, mRenrenUserTimeLineListener);
}
private com.renren.api.connect.android.RequestListener mRenrenUserTimeLineListener = new com.renren.api.connect.android.RequestListener() {
@Override
public void onRenrenError(RenrenError renrenError) {
taskHelper.popTask("Renren");
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 人人信息获取发生未知错误");
}
}
@Override
public void onFault(Throwable fault) {
taskHelper.popTask("Renren");
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 人人信息获取发生未知错误");
}
}
@Override
public void onComplete(String response) {
try {
JSONArray statuses = new JSONArray(response);
if (statuses != null) {
for (int i = 0; i < statuses.length(); i++) {
JSONObject ob = statuses.getJSONObject(i);
ItemViewModel model = RenrenConverter
.convertStatusToCommon(ob, mainViewModel);
if (model != null) {
mainViewModel.renrenItems.add(model);
}
}
}
taskHelper.popTask("Renren");
} catch (Exception e) {
taskHelper.popTask("Renren");
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 人人信息获取发生未知错误");
}
}
}
};
private void refreshModelDouban() {
mainViewModel.doubanItems.clear();
mainViewModel.doubanPictureItems.clear();
// 1.判断是否登陆
Boolean isLoggin = MiscTool.isDoubanLogin();
if (!isLoggin) {
taskHelper.popTask("Douban");
return;
}
// 2.判断是否关注了用户
final String strFollowerID = PreferenceHelper
.getString("Douban_FollowerID");
if (StringTool.isNullOrEmpty(strFollowerID)) {
taskHelper.popTask("Douban");
return;
}
// 3. 依据是否过期,抓timeline
long exp = PreferenceHelper.getLong("Douban_ExpirationDate");
// 3.1 如果过期,尝试用refresh_token来刷新
if (exp < System.currentTimeMillis()) {
final String refreshToken = PreferenceHelper.getString("Douban_RefreshToken");
// 有refresh_token的情况下,开始做刷新token工作
if(!StringTool.isNullOrEmpty(refreshToken))
{
new Thread(new Runnable() {
@Override
public void run() {
try {
OAuthDoubanProvider provider = new OAuthDoubanProvider();
AccessToken newAccessToken = provider.tradeAccessTokenWithRefreshToken(refreshToken);
final String token = newAccessToken.getAccessToken();
Integer expires_in = newAccessToken.getExpiresIn();
String refresh_token = newAccessToken.getRefreshToken();
long exp = System.currentTimeMillis() + expires_in * 1000;
SharedPreferences pref = App.getAppContext().getSharedPreferences(
AppConstants.PREFERENCES_NAME, Context.MODE_APPEND);
Editor editor = pref.edit();
editor.putString("Douban_Token", token);
editor.putString("Douban_RefreshToken", refresh_token);
editor.putLong("Douban_ExpirationDate", exp);
editor.commit();
// 至此,说明换token成功,开始重新refreshModelDouban
refreshModelDouban();
return;
} catch (Exception e) {
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 豆瓣帐号授权已过期,请到帐号页重新登陆以授权");
PreferenceHelper.removeDoubanPreference();
}
taskHelper.popTask("Douban");
return;
}
}
}).start();
return;
}
// 要是连refresh_token都没有,直接返回,报过期。正常情况下,不应该进入这里才对
else
{
// 走到这里,说明
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 豆瓣帐号授权已过期,请到帐号页重新登陆以授权");
PreferenceHelper.removeDoubanPreference();
}
taskHelper.popTask("Douban");
return;
}
}
// 3.2 若没有过期,抓 timeline
else
{
final String token = PreferenceHelper.getString("Douban_Token");
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpManager httpManager = new HttpManager(token);
String url = String.format(
"%s/shuo/v2/statuses/user_timeline/%s",
DefaultConfigs.API_URL_PREFIX, strFollowerID);
List<NameValuePair> params = new ArrayList<NameValuePair>();
if(mType == FORGRAOUND)
params.add(new BasicNameValuePair("count", "60")); // 豆瓣最大一次加载200
else
params.add(new BasicNameValuePair("count", "5")); // 后台轮询的话,要最大化节省流量 ^_^ 我真是个好人
String result = httpManager.getResponseString(url, params,
true);
JSONArray statuses = new JSONArray(result);
if (statuses != null) {
for (int i = 0; i < statuses.length(); i++) {
JSONObject ob = statuses.getJSONObject(i);
ItemViewModel model = DoubanConverter
.convertUnionStatus(ob, mainViewModel);
if (model != null) {
mainViewModel.doubanItems.add(model);
}
}
}
taskHelper.popTask("Douban");
} catch (Exception e) {
e.printStackTrace();
taskHelper.popTask("Douban");
if (mType == FORGRAOUND) {
ToastHelper.show(">_< 豆瓣信息获取发生未知错误");
}
}
}
}).start();
}
}
@Override
public void onAllTaskComplete() {
isLoading = false;
refreshViewItems();
}
private void refreshViewItems() {
mainViewModel.items.clear();
mainViewModel.listItems.clear();
mainViewModel.pictureItems.clear();
mainViewModel.listPictureItems.clear();
// 1.状态部分
mainViewModel.listItems.addAll(mainViewModel.sinaWeiboItems);
mainViewModel.listItems.addAll(mainViewModel.renrenItems);
mainViewModel.listItems.addAll(mainViewModel.doubanItems);
mainViewModel.listItems.addAll(mainViewModel.rssItems);
Collections.sort(mainViewModel.listItems,
new Comparator<ItemViewModel>() {
@Override
public int compare(ItemViewModel lhs, ItemViewModel rhs) {
try {
int result = lhs.time.compareTo(rhs.time);
return -result;
} catch (Exception e) {
return 0;
}
}
});
mainViewModel.items.addAll(mainViewModel.listItems);
// 2.图片部分
mainViewModel.listPictureItems
.addAll(mainViewModel.sinaWeiboPictureItems);
mainViewModel.listPictureItems.addAll(mainViewModel.renrenPictureItems);
mainViewModel.listPictureItems.addAll(mainViewModel.doubanPictureItems);
Collections.sort(mainViewModel.listPictureItems,
new Comparator<PictureItemViewModel>() {
@Override
public int compare(PictureItemViewModel lhs,
PictureItemViewModel rhs) {
try {
int result = lhs.time.compareTo(rhs.time);
return -result;
} catch (Exception e) {
return 0;
}
}
});
mainViewModel.pictureItems.addAll(mainViewModel.listPictureItems);
// 3.存缓存
try {
File myDir = App.getAppContext().getFilesDir();
File cacheFile = new File(myDir, AppConstants.CACHE_ITEM);
FileOutputStream fos = new FileOutputStream(cacheFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(mainViewModel.items);
oos.close();
File cacheFile2 = new File(myDir, AppConstants.CACHE_PIC_ITEM);
FileOutputStream fos2 = new FileOutputStream(cacheFile2);
ObjectOutputStream oos2 = new ObjectOutputStream(fos2);
oos2.writeObject(mainViewModel.pictureItems);
oos2.close();
} catch (Exception e) {
e.printStackTrace();
}
// 4.后台轮询服务相关
// 4.1
// 如果是FORGROUND,清掉Notification
if (mType == FORGRAOUND) {
try {
NotificationManager mNotificationManager = (NotificationManager) App
.getAppContext().getSystemService(
Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(NewsPollingService.NOTIFICATION_ID);
} catch (Exception e) {
e.printStackTrace();
}
}
// 4.2找到第一个time非空的项,更新最新消息的参数项,方便后台service判断是否有更新
// 前台refresh和后台refresh的最新item时间点是分别保存的
SharedPreferences pref = App.getAppContext().getSharedPreferences(
AppConstants.PREFERENCES_NAME, Context.MODE_APPEND | Context.MODE_MULTI_PROCESS);
for (int i = 0; i < mainViewModel.items.size(); i++) {
ItemViewModel item = mainViewModel.items.get(i);
if (item != null && item.time != null) {
if (mType == FORGRAOUND) {
Long lastTime = pref.getLong("Global_LastTimeLatestForegound", -1);
if (item.time.getTime() > lastTime) {
Editor editor = pref.edit();
editor.putLong("Global_LastTimeLatestForegound",
item.time.getTime());
editor.commit();
}
}
else {
Long lastTime = pref.getLong("Global_LastTimeLatestBackgound", -1);
if (item.time.getTime() > lastTime) {
Editor editor = pref.edit();
editor.putLong("Global_LastTimeLatestBackgound",
item.time.getTime());
editor.commit();
}
}
break;
}
}
notifyListeners();
}
private void notifyListeners() {
for (OnRefreshCompleteListener listener : listListeners) {
if (listener != null)
listener.onRefreshComplete();
}
isComplete = true;
}
public interface OnRefreshCompleteListener {
public void onRefreshComplete();
}
}