package org.goodev.discourse.utils;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Build;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.widget.TextView;
import org.goodev.discourse.App;
import org.goodev.discourse.BuildConfig;
import org.goodev.discourse.R;
import org.goodev.discourse.Service;
import org.goodev.discourse.api.Api;
import org.goodev.discourse.api.data.Category;
import org.goodev.discourse.api.data.UserDetails;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class Utils {
// ---- start 处理App内部链接跳转的常量
public static final String USERS = "users";
public static final String T = "t";
public static final String COLOR_PREFIX = "#";
// ---- end 处理App内部链接跳转的常量
public static final String AVATAR_HTTP_PREFIX = "http:";
public static final String HTTP_PREFIX = "http://";
public static final String HTTPS_PREFIX = "https://";
public static final String SLASH2 = "//";
public static final String SLASH = "/";
public static final String JSON = ".json";
public static final String TRACK_VISIT = "?track_visit=true";
public static final String TOPIC = "t";
public static final String POSTS = "posts.json";
public static final String POSTS_ID = "post_ids[]";
public static final String EQ = "=";
public static final String MARK = "?";
public static final String AND = "&";
public static final int PAGE_COUNT = 20;
public static final String EXTRA_SLUG = "extra_slug";
public static final String EXTRA_ID = "extra_id";
public static final String EXTRA_TITLE = "extra_title";
public static final String EXTRA_URL = "extra_url";
public static final String EXTRA_TYPE = "extra_type";
public static final String EXTRA_MSG = "extra_msg";
public static final String EXTRA_NUMBER = "extra_number";
public static final String EXTRA_CAT_INDEX = "extra_cat_index";
public static final String EXTRA_STATUS_CODE = "extra_code";
public static final String EXTRA_CALLBACK = "extra_callback";
public static final String EXTRA_NAME = "extra_name";
public static final String EXTRA_IS_EDIT_POST = "extra_edit_post";
public static final String EXTRA_IS_PRIVATE_MSG = "extra_private_msg";
public static final String EXTRA_OBJ = "extra_obj";
public static final String EXTRA_LINKS = "extra_links";
public static final String EXTRA_OBJ_C = "extra_objc";
public static final String EXTRA_PASSWORD = "extra_password";
public static final int ANIMATION_FADE_IN_TIME = 250;
/**
* 30天的时间
*/
public static final long DAY_30 = 30 * 24 * 60 * 60 * 1000L;
public static final String GRAVATAR_PREFIX = "//www.gravatar.com";
public static final String GRAVATAR_HTTP_PREFIX = "http:";
public static final String GRAVATAR_SIZE = "{size}";
public static final String TEXT = "<p>I know I'd like to visit various new Discourse servers once <blockquote>in awhile</blockquote> as they come up. As you'd like a visitor or two, please post your URL here and a description of the purpose of your site and any other notes...</p> <p>So far, the public Discourse servers I know about are:</p> <ul> <li> <a href=\"http://meta.discourse.org\">meta.discourse.org</a> [This one]</li> <li> <a href=\"http://try.discourse.org\">try.discourse.org</a> [daily-reset sandbox]</li> <li> <a href=\"http://discourse.jcsims.me/\" rel=\"nofollow\">discourse.jcsims.me</a> Engineering and Computer Science Interest Group - University of Rhode Island by <a href=\"/users/jcsims\" class=\"mention\">@jcsims</a> </li> <li> <a href=\"http://bitcoindiscourse.com\" rel=\"nofollow\">bitcoindiscourse.com</a> about BitCoins by <a href=\"/users/ian_purton\" class=\"mention\">@ian_purton</a> </li> <li> <a href=\"http://forums.ctrlcmdesc.com/\" rel=\"nofollow\">forums.ctrlcmdesc.com/</a> Control Command Escape (Mac Games) by <a href=\"/users/ninjafoodstuff\" class=\"mention\">@NinjaFoodstuff</a> </li> <li> <a href=\"http://answers.joomlart.com/\" rel=\"nofollow\">answers.joomlart.com/</a> by <a href=\"/users/hung_dinh\" class=\"mention\">@Hung_Dinh</a> </li> <li> <a href=\"http://forum.greenheartgames.com/\" rel=\"nofollow\">forum.greenheartgames.com</a> by <a href=\"/users/pakl\" class=\"mention\">@pakl</a> </li> </ul>";
private final static SimpleDateFormat mPostDateFormat = new SimpleDateFormat("dd MMM", Locale.getDefault());
public static String ENCODED_POSTS_ID;
static {
try {
ENCODED_POSTS_ID = URLEncoder.encode(POSTS_ID, "UTF-8");
} catch (UnsupportedEncodingException e) {
ENCODED_POSTS_ID = POSTS_ID;
}
}
public static long ONE_MIN = 1000 * 60;
public static long ONE_HOUR = 1000 * 60 * 60;
public static long ONE_DAY = 1000 * 60 * 60 * 24;
private static NumberFormat nf = new DecimalFormat("#.#K");
private static double THOUSAND = 1000.0;
public static final int parseColor(String color) {
if (!TextUtils.isEmpty(color)) {
if (color.length() == 3) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < 3; i++) {
b.append(color.charAt(i));
b.append(color.charAt(i));
}
color = b.toString();
}
color = Utils.COLOR_PREFIX + color;
}
try {
return Color.parseColor(color);
} catch (Exception e) {
}
return Color.WHITE;
}
public static final Intent getService(Context ctx, String siteUrl, int type) {
Intent intent = new Intent();
intent.setClass(ctx, Service.class);
intent.putExtra(Utils.EXTRA_TYPE, type);
intent.putExtra(Utils.EXTRA_URL, siteUrl);
return intent;
}
public static void loge(String msg, Exception e) {
if (BuildConfig.DEBUG)
Log.e("DiscourseException", msg, e);
}
public static void logi(String msg, Exception e) {
if (BuildConfig.DEBUG)
Log.i("DiscourseException", msg, e);
}
public static void loge(Exception e) {
if (BuildConfig.DEBUG)
Log.e("DiscourseException", e.getMessage(), e);
}
// ---- 格式化显示 第一帖和最后一贴日期
/**
* format topics list first post time and last post time
*
* @param time
* @return
*/
public static CharSequence formatActivityTime(long time) {
return DateUtils.formatSameDayTime(time, System.currentTimeMillis(), DateFormat.MEDIUM, DateFormat.MEDIUM);
}
// /t/4116/posts.json?post_ids[]=15557&post_ids[]=15558&post_ids[]=15560
public static String buildMorePostsUrl(String site, Long id, Long[] streams, int index) {
StringBuilder sb = new StringBuilder(site);
sb.append(TOPIC).append(SLASH).append(id.longValue()).append(SLASH).append(POSTS).append(MARK);
for (int i = index; i < streams.length && i < index + PAGE_COUNT; i++) {
sb.append(ENCODED_POSTS_ID).append(EQ).append(streams[i]).append(AND);
}
return sb.toString();
}
// t/discourse/549
public static String buildPostsUrl(String site, Long id, String slug) {
StringBuilder sb = new StringBuilder(site);
sb.append(TOPIC).append(SLASH).append(slug).append(SLASH).append(id.longValue()).append(JSON).append(TRACK_VISIT);
return sb.toString();
}
public static String removeLastSlash(String url) {
if (url.endsWith(Utils.SLASH)) {
return url.substring(0, url.length() - 1);
}
return url;
}
public static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
public static boolean hasHoneycombMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;
}
public static boolean hasICS() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
}
public static boolean hasJellyBeanMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public static String getUserAgent(Context context) {
return "Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/29.0.1547.66 m";
}
public static boolean isTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
public static boolean isHoneycombTablet(Context context) {
return hasHoneycomb() && isTablet(context);
}
public static boolean isToday(long when) {
Time time = new Time();
time.set(when);
int thenYear = time.year;
int thenMonth = time.month;
int thenMonthDay = time.monthDay;
time.set(System.currentTimeMillis());
return (thenYear == time.year) && (thenMonth == time.month) && (thenMonthDay == time.monthDay);
}
public static boolean isInOneMonth(long when) {
Time time = new Time();
time.set(when + DAY_30);
Time now = new Time();
now.setToNow();
if (time.before(now)) {
return false;
}
return true;
}
public static final CharSequence formatPostTime(long then) {
CharSequence s = DateUtils.getRelativeTimeSpanString(then, System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS, DateUtils.FORMAT_ABBREV_ALL);
return s;
// if (isToday(then)) {
// return getTodayPostTimeDisplay(then);
// } else if (isInOneMonth(then)) {
// return getInOneMonthPostTimeDisplay(then);
// } else {
// return getOutOneMonthPostTimeDisplay(then);
// }
}
private static CharSequence getTodayPostTimeDisplay(long then) {
long now = System.currentTimeMillis();
long delta = (now - then) / ONE_HOUR;
if (delta > 0) {
return App.getContext().getString(R.string.post_time_in_hour, delta);
}
delta = (now - then) / ONE_MIN;
if (delta > 0) {
return App.getContext().getString(R.string.post_time_in_min, delta);
}
return App.getContext().getString(R.string.post_time_in_sec);
}
private static CharSequence getOutOneMonthPostTimeDisplay(long then) {
return mPostDateFormat.format(new Date(then));
}
private static CharSequence getInOneMonthPostTimeDisplay(long then) {
long timeOne = then;
long timeTwo = System.currentTimeMillis();
long delta = (timeTwo - timeOne) / ONE_DAY;
return App.getContext().getString(R.string.post_time_in_day, delta);
}
public static final String getAvatarUrl(String template, int size) {
if (TextUtils.isEmpty(template)) {
return null;
}
if (template.startsWith(GRAVATAR_PREFIX)) {
template = template.replace(GRAVATAR_SIZE, size + "");
return GRAVATAR_HTTP_PREFIX + template;
}
if(!template.startsWith(App.getSiteUrl())) {
template = App.getSiteUrl() + template;
}
return template.replace(GRAVATAR_SIZE, size + "");
}
public static final boolean checkLoginInfo(String site, String name, String password) {
try {
CookieManager cm = App.getCookieManager();
URL url = new URL(site + Api.CSRF_URL);
URLConnection conn = url.openConnection();
conn.connect();
cm.storeCookies(conn);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null)
response.append(inputLine);
in.close();
String csrfs = response.toString();
JSONObject csrf = new JSONObject(csrfs);
String csrfV = csrf.getString("csrf");
Map<String, String> data = new HashMap<String, String>();
data.put("login", name);
data.put("password", password);
data.put("authenticity_token", csrfV);
// login=rain_hust&password=147852
HttpRequest r = HttpRequest.post(site + Api.SESSION_URL);
HttpURLConnection con = r.getConnection();
cm.setCookies(con);
r.header("X-CSRF-Token", csrfV).
header("Content-Type", "application/json").
form(data);
String user = r.body();
cm.storeCookies(con);
HttpRequest r2 = HttpRequest.post(site + Api.LOGIN_URL);
HttpURLConnection con2 = r2.getConnection();
cm.setCookies(con2);
r2.header("X-CSRF-Token", csrfV).
header("Content-Type", "application/json").
form(data);
String user2 = r2.body();
cm.storeCookies(con2);
HttpRequest g = HttpRequest.get(site + String.format(Api.MSG_URL, name));
HttpURLConnection con3 = g.getConnection();
cm.setCookies(con3);
g.header("X-CSRF-Token", csrfV);
String b = g.body();
try {
JSONObject rr = new JSONObject(b);
} catch (JSONException e) {
return false;
}
return true;
} catch (Exception ioe) {
ioe.printStackTrace();
return false;
}
}
public static final UserDetails login(String site, String name, String password) {
try {
CookieManager cm = App.getCookieManager();
URL url = new URL(site + Api.CSRF_URL);
URLConnection conn = url.openConnection();
conn.connect();
cm.storeCookies(conn);
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null)
response.append(inputLine);
in.close();
String csrfs = response.toString();
JSONObject csrf = new JSONObject(csrfs);
String csrfV = csrf.getString("csrf");
Map<String, String> data = new HashMap<String, String>();
L.i("login: name %s : pwd: %s csrfv: %s", name, password, csrfV);
data.put("login", name);
data.put("password", password);
data.put("authenticity_token", csrfV);
App.setXsrToken(csrfV);
// login=rain_hust&password=147852
// 返回用户数据
HttpRequest r = HttpRequest.post(site + Api.SESSION_URL);
HttpURLConnection con = r.getConnection();
cm.setCookies(con);
r.header("X-CSRF-Token", csrfV).
header("Content-Type", "application/json").
form(data);
String user = r.body();
cm.storeCookies(con);
L.d(user);
// 返回首页网页html内容
HttpRequest r2 = HttpRequest.post(site + Api.LOGIN_URL);
HttpURLConnection con2 = r2.getConnection();
cm.setCookies(con2);
r2.header("X-CSRF-Token", csrfV).
header("Content-Type", "application/json").
form(data);
String user2 = r2.body();
cm.storeCookies(con2);
JSONObject obj = new JSONObject(user);
if (obj.has(Api.K_user)) {
obj = obj.getJSONObject(Api.K_user);
UserDetails result = Api.getJSONObject(obj, UserDetails.class);
return result;
}
} catch (Exception ioe) {
ioe.printStackTrace();
}
return null;
}
public static final void setCategoryView(Category c, TextView view) {
Category cat = c;
if (cat == null) {
view.setText(null);
view.setBackgroundColor(Color.TRANSPARENT);
} else {
setCategoryView(view, cat.name, cat.color, cat.text_color);
}
}
public static final void setCategoryView(TextView view, String name, String bgColor, String textColor) {
view.setText(name);
int color = Utils.parseColor(bgColor);
int tc = Utils.parseColor(textColor);
if (color == tc) {
tc = Color.WHITE;
}
view.setBackgroundColor(color);
view.setTextColor(tc);
}
public static final void cancelTask(@SuppressWarnings("rawtypes") AsyncTask task) {
if (task != null && task.isCancelled()) {
task.cancel(true);
}
}
public static boolean isFirstRun() {
Context ctx = App.getContext();
SharedPreferences pref = ctx.getSharedPreferences("first_run", Context.MODE_PRIVATE);
int version = ctx.getResources().getInteger(R.integer.app_version_code);
final String key = "version" + version;
boolean first = pref.getBoolean(key, true);
if (first) {
pref.edit().putBoolean(key, false).commit();
}
return first;
}
public static String formatTopicViews(long views) {
if (views < 1000) {
return String.valueOf(views);
}
return nf.format(views / THOUSAND);
}
/**
* 返回未读主题标题后面的 星号 ※
*
* @param title
* @return
*/
public static CharSequence getNewTitleSpan(String title) {
Spannable span = SpannableStringBuilder.valueOf(title + Api.NEW_SIGN);
int start = title.length();
int end = span.length();
// ff0099cc
span.setSpan(new ForegroundColorSpan(0xff0099cc), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new AbsoluteSizeSpan(22, true), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return span;
}
public final static boolean isValidEmail(CharSequence target) {
if (target == null) {
return false;
} else {
return android.util.Patterns.EMAIL_ADDRESS.matcher(target).matches();
}
}
}