Java tutorial
/* * Copyright 2015 Hippo Seven * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hippo.nimingban.client.ac; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.webkit.MimeTypeMap; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.hippo.io.FileInputStreamPipe; import com.hippo.nimingban.NMBAppConfig; import com.hippo.nimingban.client.CancelledException; import com.hippo.nimingban.client.NMBException; import com.hippo.nimingban.client.ac.data.ACFeed; import com.hippo.nimingban.client.ac.data.ACForumGroup; import com.hippo.nimingban.client.ac.data.ACPost; import com.hippo.nimingban.client.ac.data.ACPostStruct; import com.hippo.nimingban.client.ac.data.ACReference; import com.hippo.nimingban.client.ac.data.ACReplyStruct; import com.hippo.nimingban.client.ac.data.ACSearchItem; import com.hippo.nimingban.client.data.ACSite; import com.hippo.nimingban.client.data.CommonPost; import com.hippo.nimingban.client.data.DumpSite; import com.hippo.nimingban.client.data.Post; import com.hippo.nimingban.client.data.Reply; import com.hippo.nimingban.util.BitmapUtils; import com.hippo.okhttp.GoodRequestBuilder; import com.hippo.okhttp.ResponseUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.StringUtils; import com.hippo.yorozuya.io.InputStreamPipe; import com.squareup.okhttp.Call; import com.squareup.okhttp.Headers; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.MultipartBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; public class ACEngine { private static final String TAG = ACEngine.class.getSimpleName(); private static final MediaType MEDIA_TYPE_IMAGE_ALL = MediaType.parse("image/*"); private static final MediaType MEDIA_TYPE_IMAGE_JPEG = MediaType.parse("image/jpeg"); private static final String UNKNOWN = "Unknown"; private static void throwException(Call call, String body, Exception e) throws Exception { if (call.isCanceled()) { throw new CancelledException(); } if (e instanceof NMBException) { if (!UNKNOWN.equals(e.getMessage())) { throw e; } } if (TextUtils.isEmpty(body)) { return; } try { JSONObject jo = JSON.parseObject(body); if (!jo.getBoolean("success")) { throw new NMBException(ACSite.getInstance(), jo.getString("msg")); } } catch (Exception ee) { // Ignore } Document doc = Jsoup.parse(body); List<Element> elements = doc.getElementsByClass("error"); if (!elements.isEmpty()) { throw new NMBException(ACSite.getInstance(), elements.get(0).text()); } } public static Call prepareGetCookie(OkHttpClient okHttpClient) { String url = ACUrl.API_GET_COOKIE; Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static Boolean doGetCookie(Call call) throws Exception { String body = null; try { Response response = call.execute(); ResponseUtils.storeCookies(response); body = response.body().string(); if (!"\"ok\"".equals(body)) { throw new NMBException(ACSite.getInstance(), UNKNOWN); } return true; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetCommonPosts(OkHttpClient okHttpClient) { String url = ACUrl.API_COMMON_POSTS; Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static List<CommonPost> doGetCommonPosts(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); List<CommonPost> result = JSON.parseArray(body, CommonPost.class); if (result == null) { throw new NMBException(ACSite.getInstance(), "Can't parse json when getForumList"); } return result; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetForumList(OkHttpClient okHttpClient) { String url = ACUrl.API_GET_FORUM_LIST; Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static List<ACForumGroup> doGetForumList(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); List<ACForumGroup> result = JSON.parseArray(body, ACForumGroup.class); if (result == null) { throw new NMBException(ACSite.getInstance(), "Can't parse json when getForumList"); } return result; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetPostList(OkHttpClient okHttpClient, String id, int page) { String url = ACUrl.getPostListUrl(id, page); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static List<Post> doGetPostList(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); List<ACPost> acPosts = JSON.parseArray(body, ACPost.class); if (acPosts == null) { throw new NMBException(ACSite.getInstance(), "Can't parse json when getPostList"); } List<Post> result = new ArrayList<>(acPosts.size()); for (ACPost acPost : acPosts) { if (acPost != null) { acPost.generateSelfAndReplies(ACSite.getInstance()); result.add(acPost); } } return result; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetPost(OkHttpClient okHttpClient, String id, int page) { String url = ACUrl.getPostUrl(id, page); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static Pair<Post, List<Reply>> doGetPost(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); ACPost acPost = JSON.parseObject(body, ACPost.class); if (acPost == null) { throw new NMBException(ACSite.getInstance(), "Can't parse json when getPost"); } acPost.generateSelfAndReplies(ACSite.getInstance()); return new Pair<Post, List<Reply>>(acPost, new ArrayList<Reply>(acPost.replys)); } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetReference(OkHttpClient okHttpClient, String id) { String url = ACUrl.getReferenceUrl(id); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static Reply doGetReference(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); ACReference reference = new ACReference(); Document doc = Jsoup.parse(body, ACUrl.HOST + "/"); List<Element> elements = doc.getAllElements(); for (Element element : elements) { String className = element.className(); if ("h-threads-item-reply h-threads-item-ref".equals(className)) { reference.id = element.attr("data-threads-id"); } else if ("h-threads-img-a".equals(className)) { reference.image = element.attr("href"); } else if ("h-threads-img".equals(className)) { reference.thumb = element.attr("src"); } else if ("h-threads-info-title".equals(className)) { reference.title = element.text(); } else if ("h-threads-info-email".equals(className)) { // TODO email or user ? reference.user = element.text(); } else if ("h-threads-info-createdat".equals(className)) { reference.time = element.text(); } else if ("h-threads-info-uid".equals(className)) { String user = element.text(); if (user.startsWith("ID:")) { reference.userId = user.substring(3); } else { reference.userId = user; } reference.admin = element.childNodeSize() > 1; } else if ("h-threads-info-id".equals(className)) { String href = element.attr("href"); if (href.startsWith("/t/")) { int index = href.indexOf('?'); if (index >= 0) { reference.postId = href.substring(3, index); } else { reference.postId = href.substring(3); } } } else if ("h-threads-content".equals(className)) { reference.content = element.html(); } } reference.generate(ACSite.getInstance()); return reference; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareReply(OkHttpClient okHttpClient, ACReplyStruct struct) throws Exception { MultipartBuilder builder = new MultipartBuilder(); builder.type(MultipartBuilder.FORM); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"name\""), RequestBody.create(null, StringUtils.avoidNull(struct.name))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"email\""), RequestBody.create(null, StringUtils.avoidNull(struct.email))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"title\""), RequestBody.create(null, StringUtils.avoidNull(struct.title))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"content\""), RequestBody.create(null, StringUtils.avoidNull(struct.content))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"resto\""), RequestBody.create(null, StringUtils.avoidNull(struct.resto))); if (struct.water) { builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"water\""), RequestBody.create(null, "true")); } InputStreamPipe isPipe = struct.image; if (isPipe != null) { String filename; MediaType mediaType; byte[] bytes; File file = compressBitmap(isPipe, struct.imageType); if (file == null) { String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(struct.imageType); if (TextUtils.isEmpty(extension)) { extension = "jpg"; } filename = "a." + extension; mediaType = MediaType.parse(struct.imageType); if (mediaType == null) { mediaType = MEDIA_TYPE_IMAGE_ALL; } try { isPipe.obtain(); bytes = IOUtils.getAllByte(isPipe.open()); } finally { isPipe.close(); isPipe.release(); } } else { filename = "a.jpg"; mediaType = MEDIA_TYPE_IMAGE_JPEG; InputStream is = null; try { is = new FileInputStream(file); bytes = IOUtils.getAllByte(is); } finally { IOUtils.closeQuietly(is); file.delete(); } } builder.addPart( Headers.of("Content-Disposition", "form-data; name=\"image\"; filename=\"" + filename + "\""), RequestBody.create(mediaType, bytes)); } String url = ACUrl.API_REPLY; Log.d(TAG, url); Request request = new GoodRequestBuilder(url).post(builder.build()).build(); return okHttpClient.newCall(request); } public static Void doReply(Call call) throws Exception { String body = null; try { Response response = call.execute(); ResponseUtils.storeCookies(response); body = response.body().string(); try { JSONObject jo = JSON.parseObject(body); if (jo.getBoolean("success")) { return null; } else { throw new NMBException(ACSite.getInstance(), jo.getString("msg")); } } catch (Exception e) { if (body.contains("class=\"success\"")) { return null; } else { throw e; } } } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareGetFeed(OkHttpClient okHttpClient, String uuid, int page) { String url = ACUrl.getFeedUrl(uuid, page); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static List<Post> doGetFeed(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); List<ACFeed> acFeeds = JSON.parseArray(body, ACFeed.class); if (acFeeds == null) { throw new NMBException(ACSite.getInstance(), "Can't parse json when getPostList"); } List<Post> result = new ArrayList<>(acFeeds.size()); for (ACFeed feed : acFeeds) { if (feed != null) { feed.generate(ACSite.getInstance()); result.add(feed); } } return result; } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareAddFeed(OkHttpClient okHttpClient, String uuid, String tid) { String url = ACUrl.getAddFeedUrl(uuid, tid); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static Void doAddFeed(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); if (body.equals("\"\\u8ba2\\u9605\\u5927\\u6210\\u529f\\u2192_\\u2192\"")) { return null; } else { throw new NMBException(ACSite.getInstance(), UNKNOWN); } } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareDelFeed(OkHttpClient okHttpClient, String uuid, String tid) { String url = ACUrl.getDelFeedUrl(uuid, tid); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static Void doDelFeed(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); if (body.equals("\"\\u53d6\\u6d88\\u8ba2\\u9605\\u6210\\u529f!\"")) { return null; } else { throw new NMBException(ACSite.getInstance(), UNKNOWN); } } catch (Exception e) { throwException(call, body, e); throw e; } } public static Call prepareCreatePost(OkHttpClient okHttpClient, ACPostStruct struct) throws Exception { MultipartBuilder builder = new MultipartBuilder(); builder.type(MultipartBuilder.FORM); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"name\""), RequestBody.create(null, StringUtils.avoidNull(struct.name))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"email\""), RequestBody.create(null, StringUtils.avoidNull(struct.email))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"title\""), RequestBody.create(null, StringUtils.avoidNull(struct.title))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"content\""), RequestBody.create(null, StringUtils.avoidNull(struct.content))); builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"fid\""), RequestBody.create(null, StringUtils.avoidNull(struct.fid))); if (struct.water) { builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"water\""), RequestBody.create(null, "true")); } InputStreamPipe isPipe = struct.image; if (isPipe != null) { String filename; MediaType mediaType; byte[] bytes; File file = compressBitmap(isPipe, struct.imageType); if (file == null) { String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(struct.imageType); if (TextUtils.isEmpty(extension)) { extension = "jpg"; } filename = "a." + extension; mediaType = MediaType.parse(struct.imageType); if (mediaType == null) { mediaType = MEDIA_TYPE_IMAGE_ALL; } try { isPipe.obtain(); bytes = IOUtils.getAllByte(isPipe.open()); } finally { isPipe.close(); isPipe.release(); } } else { filename = "a.jpg"; mediaType = MEDIA_TYPE_IMAGE_JPEG; InputStream is = null; try { is = new FileInputStream(file); bytes = IOUtils.getAllByte(is); } finally { IOUtils.closeQuietly(is); file.delete(); } } builder.addPart( Headers.of("Content-Disposition", "form-data; name=\"image\"; filename=\"" + filename + "\""), RequestBody.create(mediaType, bytes)); } String url = ACUrl.API_CREATE_POST; Log.d(TAG, url); Request request = new GoodRequestBuilder(url).post(builder.build()).build(); return okHttpClient.newCall(request); } public static Void doCreatePost(Call call) throws Exception { String body = null; try { Response response = call.execute(); ResponseUtils.storeCookies(response); body = response.body().string(); try { JSONObject jo = JSON.parseObject(body); if (jo.getBoolean("success")) { return null; } else { throw new NMBException(ACSite.getInstance(), jo.getString("msg")); } } catch (Exception e) { if (body.contains("class=\"success\"")) { return null; } else { throw e; } } } catch (Exception e) { throwException(call, body, e); throw e; } } private static final long MAX_IMAGE_SIZE = 2000 * 1024; /** * @return null for not changed */ public static File compressBitmap(InputStreamPipe isp, String imageType) throws Exception { OutputStream os = null; try { isp.obtain(); File temp = NMBAppConfig.createTempFile(); if (temp == null) { throw new NMBException(DumpSite.getInstance(), "Can't create temp file"); } os = new FileOutputStream(temp); IOUtils.copy(isp.open(), os); isp.close(); os.close(); long size = temp.length(); if (size < MAX_IMAGE_SIZE && !"image/jpeg".equals(imageType) && !"image/jpg".equals(imageType)) { temp.delete(); return null; } int[] sampleScaleArray = new int[1]; BitmapUtils.decodeStream(new FileInputStreamPipe(temp), -1, -1, -1, true, true, sampleScaleArray); int sampleScale = sampleScaleArray[0]; if (sampleScale < 1) { throw new NMBException(DumpSite.getInstance(), "Can't get bitmap size"); } BitmapFactory.Options options = new BitmapFactory.Options(); int i = (int) (Math.log(sampleScale) / Math.log(2)); while (true) { options.inSampleSize = (int) Math.pow(2, i); Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeStream(isp.open(), null, options); } catch (OutOfMemoryError e) { // Ignore } if (bitmap == null) { throw new NMBException(ACSite.getInstance(), "Can't decode bitmap"); } isp.close(); os = new FileOutputStream(temp); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, os); os.close(); bitmap.recycle(); size = temp.length(); if (size < MAX_IMAGE_SIZE) { return temp; } i++; } } finally { isp.close(); isp.release(); IOUtils.closeQuietly(os); } } public static Call prepareSearch(OkHttpClient okHttpClient, String keyword, int page) throws UnsupportedEncodingException { String url = ACUrl.getSearchUrl(keyword, page); Log.d(TAG, url); Request request = new GoodRequestBuilder(url).build(); return okHttpClient.newCall(request); } public static List<ACSearchItem> doSearch(Call call) throws Exception { String body = null; try { Response response = call.execute(); body = response.body().string(); JSONArray ja = JSON.parseObject(body).getJSONObject("hits").getJSONArray("hits"); List<ACSearchItem> result = new ArrayList<>(); for (int i = 0, n = ja.size(); i < n; i++) { JSONObject jo = ja.getJSONObject(i); ACSearchItem item = jo.getObject("_source", ACSearchItem.class); item.id = jo.getString("_id"); item.generate(ACSite.getInstance()); result.add(item); } return result; } catch (Exception e) { throwException(call, body, e); throw e; } } }