migrated to RestFu

re-added streaming implementation
This commit is contained in:
Mariotaku Lee 2015-05-26 19:19:03 +08:00
parent 104691d8d0
commit 73619da1fb
150 changed files with 2043 additions and 3668 deletions

View File

@ -21,6 +21,7 @@ buildscript {
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip

View File

@ -7,5 +7,6 @@ include ':twidere.donate.nyanwp'
include ':twidere.donate.nyanwp.wear'
include ':twidere.component.nyan'
include ':twidere.extension.twitlonger'
include ':twidere.extension.streaming'
include ':twidere.extension.push.xiaomi'
include ':twidere.extension.launcher.compose'

View File

@ -37,11 +37,15 @@ android {
}
dependencies {
apt 'com.bluelinelabs:logansquare-compiler:1.0.6'
apt 'com.bluelinelabs:logansquare-compiler:1.1.0'
apt 'com.hannesdorfmann.parcelableplease:processor:1.0.1'
compile 'com.android.support:support-annotations:22.1.1'
compile 'com.android.support:support-v4:22.1.1'
compile 'com.bluelinelabs:logansquare:1.0.6'
compile 'com.bluelinelabs:logansquare:1.1.0'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'com.github.mariotaku:RestFu:6ef0913'
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4'
compile project(':twidere.component.querybuilder')
compile fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@ -1,14 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import java.lang.reflect.Type;
/**
* Created by mariotaku on 15/2/6.
*/
public interface Converter {
Object convert(RestHttpResponse response, Type type) throws Exception;
}

View File

@ -1,9 +0,0 @@
package org.mariotaku.simplerestapi;
/**
* Created by mariotaku on 15/2/7.
*/
public interface ErrorCallback {
void error(Throwable t);
}

View File

@ -1,30 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.mime.BaseTypedData;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import org.mariotaku.simplerestapi.param.File;
/**
* Created by mariotaku on 15/2/6.
*/
public final class FileValue {
private final File annotation;
private final Object value;
@Override
public String toString() {
return "FileValue{" +
"annotation=" + annotation +
", value=" + value +
'}';
}
public FileValue(File annotation, Object value) {
this.annotation = annotation;
this.value = value;
}
public TypedData body() {
return BaseTypedData.wrap(value);
}
}

View File

@ -1,13 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import java.io.IOException;
/**
* Created by mariotaku on 15/2/7.
*/
public interface RawCallback extends ErrorCallback {
void result(RestHttpResponse result) throws IOException;
}

View File

@ -1,116 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi;
import android.support.annotation.Nullable;
import android.util.Pair;
import org.mariotaku.simplerestapi.http.mime.FormTypedBody;
import org.mariotaku.simplerestapi.http.mime.MultipartTypedBody;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import org.mariotaku.simplerestapi.param.Body;
import java.util.List;
import java.util.Map;
/**
* Created by mariotaku on 15/5/11.
*/
public final class RequestInfo {
private String method;
private String path;
private List<Pair<String, String>> queries, forms, headers;
private List<Pair<String, TypedData>> parts;
private Map<String, Object> extras;
private FileValue file;
private Body body;
private TypedData bodyCache;
public RequestInfo(String method, String path, List<Pair<String, String>> queries,
List<Pair<String, String>> forms, List<Pair<String, String>> headers,
List<Pair<String, TypedData>> parts, FileValue file, Body body, Map<String, Object> extras) {
this.method = method;
this.path = path;
this.queries = queries;
this.forms = forms;
this.headers = headers;
this.parts = parts;
this.extras = extras;
this.file = file;
this.body = body;
}
public List<Pair<String, String>> getQueries() {
return queries;
}
public List<Pair<String, String>> getForms() {
return forms;
}
public List<Pair<String, String>> getHeaders() {
return headers;
}
public List<Pair<String, TypedData>> getParts() {
return parts;
}
public Map<String, Object> getExtras() {
return extras;
}
@Nullable
public TypedData getBody() {
if (bodyCache != null) return bodyCache;
if (body == null) return null;
switch (body.value()) {
case FORM: {
bodyCache = new FormTypedBody(getForms());
break;
}
case MULTIPART: {
bodyCache = new MultipartTypedBody(getParts());
break;
}
case FILE: {
bodyCache = file.body();
break;
}
}
return bodyCache;
}
public String getPath() {
return path;
}
public String getMethod() {
return method;
}
public interface Factory {
RequestInfo create(RestMethodInfo methodInfo);
}
}

View File

@ -1,188 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.simplerestapi.http.RestHttpClient;
import org.mariotaku.simplerestapi.http.RestHttpRequest;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by mariotaku on 15/2/6.
*/
public class RestAPIFactory {
private Endpoint endpoint;
private Authorization authorization;
private Converter converter;
private RestHttpClient restClient;
private RestHttpRequest.Factory requestFactory;
private ExceptionFactory exceptionFactory;
private RequestInfo.Factory requestInfoFactory;
public void setEndpoint(Endpoint endpoint) {
this.endpoint = endpoint;
}
public void setAuthorization(Authorization authorization) {
this.authorization = authorization;
}
public void setConverter(Converter converter) {
this.converter = converter;
}
public void setClient(RestHttpClient restClient) {
this.restClient = restClient;
}
public void setRequestFactory(RestHttpRequest.Factory factory) {
this.requestFactory = factory;
}
public void setExceptionFactory(ExceptionFactory factory) {
this.exceptionFactory = factory;
}
public RestAPIFactory() {
}
@SuppressWarnings("unchecked")
public <T> T build(Class<T> cls) {
final ClassLoader classLoader = cls.getClassLoader();
final Class[] interfaces = new Class[]{cls};
return (T) Proxy.newProxyInstance(classLoader, interfaces, new RestInvocationHandler(endpoint,
authorization, restClient, converter, requestInfoFactory, requestFactory, exceptionFactory));
}
public static RestClient getRestClient(Object obj) {
final InvocationHandler handler = Proxy.getInvocationHandler(obj);
if (!(handler instanceof RestClient)) throw new IllegalArgumentException();
return (RestClient) handler;
}
public void setRequestInfoFactory(RequestInfo.Factory requestInfoFactory) {
this.requestInfoFactory = requestInfoFactory;
}
private static class RestInvocationHandler implements InvocationHandler, RestClient {
private final Endpoint endpoint;
private final Authorization authorization;
private final Converter converter;
private final RestHttpRequest.Factory requestFactory;
private final ExceptionFactory exceptionFactory;
private final RequestInfo.Factory requestInfoFactory;
@Override
public Endpoint getEndpoint() {
return endpoint;
}
@Override
public RestHttpClient getRestClient() {
return restClient;
}
@Override
public Converter getConverter() {
return converter;
}
@Override
public Authorization getAuthorization() {
return authorization;
}
private final RestHttpClient restClient;
public RestInvocationHandler(Endpoint endpoint, Authorization authorization,
RestHttpClient restClient, Converter converter,
RequestInfo.Factory requestInfoFactory, RestHttpRequest.Factory requestFactory, ExceptionFactory exceptionFactory) {
this.endpoint = endpoint;
this.authorization = authorization;
this.restClient = restClient;
this.converter = converter;
this.requestInfoFactory = requestInfoFactory;
this.requestFactory = requestFactory != null ? requestFactory : new RestHttpRequest.DefaultFactory();
this.exceptionFactory = exceptionFactory != null ? exceptionFactory : new DefaultExceptionFactory();
}
@SuppressWarnings("TryWithIdenticalCatches")
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
final RestMethodInfo methodInfo = RestMethodInfo.get(method, args);
final RequestInfo requestInfo;
if (requestInfoFactory != null) {
requestInfo = requestInfoFactory.create(methodInfo);
} else {
requestInfo = methodInfo.toRequestInfo();
}
final RestHttpRequest restHttpRequest = requestFactory.create(endpoint, requestInfo, authorization);
final Class<?>[] parameterTypes = method.getParameterTypes();
RestHttpResponse response = null;
try {
response = restClient.execute(restHttpRequest);
if (parameterTypes.length > 0) {
final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
if (RestCallback.class.isAssignableFrom(lastParameterType)) {
final Method callbackMethod = lastParameterType.getMethod("result", Object.class);
final RestCallback<?> callback = (RestCallback<?>) args[args.length - 1];
final Object object = converter.convert(response, method.getGenericReturnType());
if (callback != null) {
callbackMethod.invoke(callback, object);
}
return null;
} else if (RawCallback.class.isAssignableFrom(lastParameterType)) {
final RawCallback callback = (RawCallback) args[args.length - 1];
callback.result(response);
return null;
}
}
return converter.convert(response, method.getGenericReturnType());
} catch (IOException e) {
final Exception re = exceptionFactory.newException(e, response);
if (parameterTypes.length > 0) {
final Class<?> lastParameterType = parameterTypes[parameterTypes.length - 1];
if (ErrorCallback.class.isAssignableFrom(lastParameterType)) {
final ErrorCallback callback = (ErrorCallback) args[args.length - 1];
if (callback != null) {
callback.error(re);
return null;
}
}
}
throw re;
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} finally {
Utils.closeSilently(response);
}
}
}
public interface ExceptionFactory {
Exception newException(Throwable cause, RestHttpResponse response);
}
public static final class DefaultExceptionFactory implements ExceptionFactory {
@Override
public Exception newException(Throwable cause, RestHttpResponse response) {
final RestException e = new RestException(cause);
e.setResponse(response);
return e;
}
}
}

View File

@ -1,9 +0,0 @@
package org.mariotaku.simplerestapi;
/**
* Created by mariotaku on 15/2/7.
*/
public interface RestCallback<T> extends ErrorCallback {
void result(T result);
}

View File

@ -1,19 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.simplerestapi.http.RestHttpClient;
/**
* Created by mariotaku on 15/4/19.
*/
public interface RestClient {
Endpoint getEndpoint();
RestHttpClient getRestClient();
Converter getConverter();
Authorization getAuthorization();
}

View File

@ -1,40 +0,0 @@
package org.mariotaku.simplerestapi;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
/**
* Created by mariotaku on 15/2/7.
*/
public class RestException extends RuntimeException {
public RestException(String message, Throwable cause) {
super(message, cause);
}
public RestException(String message) {
super(message);
}
public RestException() {
}
public RestException(Throwable cause) {
super(cause);
}
private RestHttpResponse response;
@Override
public String toString() {
return "RestException{" +
"response=" + response +
"} " + super.toString();
}
public RestHttpResponse getResponse() {
return response;
}
public void setResponse(RestHttpResponse response) {
this.response = response;
}
}

View File

@ -1,18 +0,0 @@
package org.mariotaku.simplerestapi;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestMethod {
String value();
boolean hasBody() default false;
}

View File

@ -1,339 +0,0 @@
package org.mariotaku.simplerestapi;
import android.support.annotation.NonNull;
import android.util.Pair;
import org.apache.commons.lang3.NotImplementedException;
import org.mariotaku.simplerestapi.http.ValueMap;
import org.mariotaku.simplerestapi.http.mime.BaseTypedData;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Extra;
import org.mariotaku.simplerestapi.param.File;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Header;
import org.mariotaku.simplerestapi.param.Part;
import org.mariotaku.simplerestapi.param.Path;
import org.mariotaku.simplerestapi.param.Query;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by mariotaku on 15/2/6.
*/
public final class RestMethodInfo {
private final RestMethod method;
private final String path;
private final Body body;
private final HashMap<Path, Object> paths;
private final HashMap<Query, Object> queries;
private final HashMap<Header, Object> headers;
private final HashMap<Form, Object> forms;
private final HashMap<Part, Object> parts;
private final HashMap<Extra, Object> extras;
private final FileValue file;
private ArrayList<Pair<String, String>> queriesCache, formsCache, headersCache;
private ArrayList<Pair<String, TypedData>> partsCache;
private Map<String, Object> extrasCache;
private TypedData bodyCache;
RestMethodInfo(final RestMethod method, String path, final Body body, final HashMap<Path, Object> paths, final HashMap<Query, Object> queries,
final HashMap<Header, Object> headers, final HashMap<Form, Object> forms, final HashMap<Part, Object> parts,
final FileValue file, HashMap<Extra, Object> extras) {
this.method = method;
this.path = path;
this.body = body;
this.paths = paths;
this.queries = queries;
this.headers = headers;
this.forms = forms;
this.parts = parts;
this.extras = extras;
this.file = file;
}
static RestMethodInfo get(Method method, Object[] args) {
RestMethod restMethod = null;
String pathFormat = null;
for (Annotation annotation : method.getAnnotations()) {
final Class<?> annotationType = annotation.annotationType();
restMethod = annotationType.getAnnotation(RestMethod.class);
if (restMethod != null) {
try {
pathFormat = (String) annotationType.getMethod("value").invoke(annotation);
} catch (Exception e) {
throw new RuntimeException(e);
}
break;
}
}
final Body body = method.getAnnotation(Body.class);
final HashMap<Path, Object> paths = new HashMap<>();
final HashMap<Query, Object> queries = new HashMap<>();
final HashMap<Header, Object> headers = new HashMap<>();
final HashMap<Form, Object> forms = new HashMap<>();
final HashMap<Part, Object> parts = new HashMap<>();
final HashMap<Extra, Object> extras = new HashMap<>();
FileValue file = null;
final Annotation[][] annotations = method.getParameterAnnotations();
for (int i = 0, j = annotations.length; i < j; i++) {
final Path path = getAnnotation(annotations[i], Path.class);
if (path != null) {
paths.put(path, args[i]);
}
final Query query = getAnnotation(annotations[i], Query.class);
if (query != null) {
queries.put(query, args[i]);
}
final Header header = getAnnotation(annotations[i], Header.class);
if (header != null) {
headers.put(header, args[i]);
}
final Form form = getAnnotation(annotations[i], Form.class);
if (form != null) {
forms.put(form, args[i]);
}
final Part part = getAnnotation(annotations[i], Part.class);
if (part != null) {
parts.put(part, args[i]);
}
final File paramFile = getAnnotation(annotations[i], File.class);
if (paramFile != null) {
if (file == null) {
file = new FileValue(paramFile, args[i]);
} else {
throw new IllegalArgumentException();
}
}
final Extra extra = getAnnotation(annotations[i], Extra.class);
if (extra != null) {
extras.put(extra, args[i]);
}
}
checkMethod(restMethod, body, forms, parts, file);
return new RestMethodInfo(restMethod, pathFormat, body, paths, queries, headers, forms, parts, file, extras);
}
private static String[] getValueMapKeys(String[] annotationValue, ValueMap valueMap) {
return annotationValue != null && annotationValue.length > 0 ? annotationValue : valueMap.keys();
}
private static void checkMethod(RestMethod restMethod, Body body, HashMap<Form, Object> forms, HashMap<Part, Object> parts, FileValue file) {
if (restMethod == null)
throw new NotImplementedException("Method must has annotation annotated with @RestMethod");
if (restMethod.hasBody() && body == null) {
throw new IllegalArgumentException("@Body required for method " + restMethod.value());
} else if (!restMethod.hasBody() && body != null) {
throw new IllegalArgumentException(restMethod.value() + " does not allow body");
}
if (body == null) return;
switch (body.value()) {
case FILE: {
if (file == null) {
throw new NullPointerException("@File annotation is required");
}
if (!forms.isEmpty() || !parts.isEmpty()) {
throw new IllegalArgumentException("Only arguments with @File annotation allowed");
}
break;
}
case MULTIPART: {
if (!forms.isEmpty() || file != null) {
throw new IllegalArgumentException("Only arguments with @Part annotation allowed");
}
break;
}
case FORM: {
if (file != null || !parts.isEmpty()) {
throw new IllegalArgumentException("Only arguments with @Form annotation allowed");
}
break;
}
}
}
@SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotation(Annotation[] annotations, Class<T> annotationClass) {
for (Annotation annotation : annotations) {
if (annotationClass.isAssignableFrom(annotation.annotationType())) {
return (T) annotation;
}
}
return null;
}
@NonNull
public Map<String, Object> getExtras() {
if (extrasCache != null) return extrasCache;
final Map<String, Object> map = new HashMap<>();
for (Map.Entry<Extra, Object> entry : extras.entrySet()) {
final Extra extra = entry.getKey();
final Object value = entry.getValue();
if (value instanceof ValueMap) {
final ValueMap valueMap = (ValueMap) value;
for (String key : getValueMapKeys(extra.value(), valueMap)) {
if (valueMap.has(key)) {
map.put(key, valueMap.get(key));
}
}
} else if (value != null) {
for (String key : extra.value()) {
map.put(key, value);
}
}
}
return extrasCache = map;
}
@NonNull
public List<Pair<String, String>> getForms() {
if (formsCache != null) return formsCache;
final ArrayList<Pair<String, String>> list = new ArrayList<>();
for (Map.Entry<Form, Object> entry : forms.entrySet()) {
final Form form = entry.getKey();
final Object value = entry.getValue();
if (value == null) continue;
if (value instanceof ValueMap) {
final ValueMap valueMap = (ValueMap) value;
for (String key : getValueMapKeys(form.value(), valueMap)) {
if (valueMap.has(key)) {
list.add(Pair.create(key, String.valueOf(valueMap.get(key))));
}
}
} else {
final char delimiter = form.arrayDelimiter();
String valueString = Utils.toString(value, delimiter);
for (String key : form.value()) {
list.add(Pair.create(key, valueString));
}
}
}
return formsCache = list;
}
@NonNull
public List<Pair<String, TypedData>> getParts() {
if (partsCache != null) return partsCache;
final ArrayList<Pair<String, TypedData>> list = new ArrayList<>();
for (Map.Entry<Part, Object> entry : parts.entrySet()) {
final Part part = entry.getKey();
final String[] names = part.value();
final Object value = entry.getValue();
if (value instanceof TypedData) {
list.add(Pair.create(names[0], (TypedData) value));
} else if (value != null) {
list.add(Pair.create(names[0], BaseTypedData.wrap(value)));
}
}
return partsCache = list;
}
@NonNull
public List<Pair<String, String>> getHeaders() {
if (headersCache != null) return headersCache;
final ArrayList<Pair<String, String>> list = new ArrayList<>();
for (Map.Entry<Header, Object> entry : headers.entrySet()) {
final Header header = entry.getKey();
final Object value = entry.getValue();
if (value instanceof ValueMap) {
final ValueMap valueMap = (ValueMap) value;
for (String key : getValueMapKeys(header.value(), valueMap)) {
if (valueMap.has(key)) {
list.add(Pair.create(key, String.valueOf(valueMap.get(key))));
}
}
} else if (value != null) {
for (String key : header.value()) {
list.add(Pair.create(key, String.valueOf(value)));
}
}
}
return headersCache = list;
}
public RestMethod getMethod() {
return method;
}
@NonNull
public String getPath() {
StringBuilder sb = new StringBuilder();
int start, end, prevEnd = -1;
while ((start = path.indexOf('{', prevEnd)) != -1 && (end = path.indexOf('}', start)) != -1) {
sb.append(path.substring(prevEnd + 1, start));
final String key = path.substring(start + 1, end);
final String replacement = findPathReplacement(key);
if (replacement == null)
throw new IllegalArgumentException("Path key {" + key + "} not bound");
sb.append(replacement);
prevEnd = end;
}
sb.append(path.substring(prevEnd + 1));
return sb.toString();
}
@NonNull
public List<Pair<String, String>> getQueries() {
if (queriesCache != null) return queriesCache;
final ArrayList<Pair<String, String>> list = new ArrayList<>();
for (Map.Entry<Query, Object> entry : queries.entrySet()) {
final Query form = entry.getKey();
final Object value = entry.getValue();
if (value == null) continue;
if (value instanceof ValueMap) {
final ValueMap valueMap = (ValueMap) value;
for (String key : getValueMapKeys(form.value(), valueMap)) {
if (valueMap.has(key)) {
list.add(Pair.create(key, String.valueOf(valueMap.get(key))));
}
}
} else {
final char delimiter = form.arrayDelimiter();
String valueString = Utils.toString(value, delimiter);
for (String key : form.value()) {
list.add(Pair.create(key, valueString));
}
}
}
return queriesCache = list;
}
private String findPathReplacement(String key) {
for (Map.Entry<Path, Object> entry : paths.entrySet()) {
final Path path = entry.getKey();
if (key.equals(path.value())) {
if (path.encoded()) {
return String.valueOf(entry.getValue());
} else {
return Utils.encode(String.valueOf(entry.getValue()), "UTF-8");
}
}
}
return null;
}
public RequestInfo toRequestInfo() {
return new RequestInfo(getMethod().value(), getPath(), getQueries(), getForms(),
getHeaders(), getParts(), getFile(), getBody(), getExtras());
}
public RequestInfo toRequestInfo(RequestInfo.Factory factory) {
return factory.create(this);
}
public Body getBody() {
return body;
}
public FileValue getFile() {
return file;
}
}

View File

@ -1,178 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
/**
* Created by mariotaku on 15/2/4.
*/
public class Utils {
public static String[] split(final String str, final String separator) {
String[] returnValue;
int index = str.indexOf(separator);
if (index == -1) {
returnValue = new String[]{str};
} else {
final List<String> strList = new ArrayList<>();
int oldIndex = 0;
while (index != -1) {
final String subStr = str.substring(oldIndex, index);
strList.add(subStr);
oldIndex = index + separator.length();
index = str.indexOf(separator, oldIndex);
}
if (oldIndex != str.length()) {
strList.add(str.substring(oldIndex));
}
returnValue = strList.toArray(new String[strList.size()]);
}
return returnValue;
}
public static void closeSilently(Closeable c) {
if (c == null) return;
try {
c.close();
} catch (IOException ignore) {
}
}
/**
* @param value string to be encoded
* @return encoded string
* @see <a href="http://wiki.oauth.net/TestCases">OAuth / TestCases</a>
* @see <a
* href="http://groups.google.com/group/oauth/browse_thread/thread/a8398d0521f4ae3d/9d79b698ab217df2?hl=en&lnk=gst&q=space+encoding#9d79b698ab217df2">Space
* encoding - OAuth | Google Groups</a>
* @see <a href="http://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 -
* Uniform Resource Identifier (URI): Generic Syntax - 2.1.
* Percent-Encoding</a>
*/
public static String encode(final String value, String encoding) {
String encoded;
try {
encoded = URLEncoder.encode(value, encoding);
} catch (final UnsupportedEncodingException ignore) {
return null;
}
final StringBuilder buf = new StringBuilder(encoded.length());
char focus;
for (int i = 0; i < encoded.length(); i++) {
focus = encoded.charAt(i);
if (focus == '*') {
buf.append("%2A");
} else if (focus == '+') {
buf.append("%20");
} else if (focus == '%' && i + 1 < encoded.length() && encoded.charAt(i + 1) == '7'
&& encoded.charAt(i + 2) == 'E') {
buf.append('~');
i += 2;
} else {
buf.append(focus);
}
}
return buf.toString();
}
public static void parseGetParameters(final String queryString, final List<Pair<String, String>> params,
final String encoding) {
final String[] queryStrings = split(queryString, "&");
try {
for (final String query : queryStrings) {
final String[] split = split(query, "=");
final String key = URLDecoder.decode(split[0], encoding);
if (split.length == 2) {
params.add(new ImmutablePair<>(key, URLDecoder.decode(split[1], encoding)));
} else {
params.add(new ImmutablePair<>(key, ""));
}
}
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static boolean copyStream(InputStream is, OutputStream os) throws IOException {
return copyStream(is, os, 8192);
}
public static boolean copyStream(InputStream is, OutputStream os, int bufferSize)
throws IOException {
final byte[] bytes = new byte[bufferSize];
int count;
while ((count = is.read(bytes, 0, bufferSize)) != -1) {
os.write(bytes, 0, count);
}
os.flush();
return true;
}
@NonNull
public static <T> T assertNotNull(@Nullable T obj) {
if (obj == null) throw new NullPointerException();
return obj;
}
private static final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
@NonNull
public static String toString(@NonNull Object value, char delimiter) {
final Class<?> valueClass = value.getClass();
if (valueClass.isArray()) {
final StringBuilder sb = new StringBuilder();
for (int i = 0, j = Array.getLength(value); i < j; i++) {
if (i != 0) {
sb.append(delimiter);
}
sb.append(Array.get(value, i));
}
return sb.toString();
} else {
return value.toString();
}
}
}

View File

@ -1,31 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi.http;
import org.mariotaku.simplerestapi.RequestInfo;
/**
* Created by mariotaku on 15/2/4.
*/
public interface Authorization {
String getHeader(Endpoint endpoint, RequestInfo info);
boolean hasAuthorization();
}

View File

@ -1,8 +0,0 @@
package org.mariotaku.simplerestapi.http;
/**
* Created by mariotaku on 15/2/6.
*/
public enum BodyType {
FORM, MULTIPART, FILE
}

View File

@ -1,122 +0,0 @@
package org.mariotaku.simplerestapi.http;
import android.util.Pair;
import org.mariotaku.simplerestapi.Utils;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* Created by mariotaku on 15/2/4.
*/
public final class ContentType {
public static final ContentType OCTET_STREAM = ContentType.parse("application/octet-stream");
private final String contentType;
private final List<Pair<String, String>> parameters;
public ContentType(String contentType, Charset charset) {
this(contentType, new ArrayList<Pair<String, String>>());
addParameter("charset", charset.name());
}
public ContentType(String contentType) {
this(contentType, new ArrayList<Pair<String, String>>());
}
public ContentType(String contentType, List<Pair<String, String>> parameters) {
this.contentType = contentType;
this.parameters = parameters;
}
public boolean addParameter(String name, String value) {
return parameters.add(Pair.create(name, value));
}
public ContentType parameter(String name, String value) {
addParameter(name, value);
return this;
}
public String parameter(String name) {
for (Pair<String, String> parameter : parameters) {
if (name.equalsIgnoreCase(parameter.first)) return parameter.second;
}
return null;
}
@Override
public String toString() {
return "ContentType{" +
"contentType='" + contentType + '\'' +
", parameters=" + parameters +
'}';
}
public Charset getCharset() {
if (parameters == null) return null;
final String charset = parameter("charset");
if (charset != null) return Charset.forName(charset);
return null;
}
public String getContentType() {
return contentType;
}
public String toHeader() {
final StringBuilder sb = new StringBuilder(contentType);
for (Pair<String, String> parameter : parameters) {
sb.append("; ");
sb.append(parameter.first);
sb.append("=");
sb.append(parameter.second);
}
return sb.toString();
}
public static ContentType parse(String string) {
final List<Pair<String, String>> parameters = new ArrayList<>();
int previousIndex = string.indexOf(';', 0);
String contentType;
if (previousIndex == -1) {
contentType = string;
} else {
contentType = string.substring(0, previousIndex);
}
while (previousIndex != -1) {
final int idx = string.indexOf(';', previousIndex + 1);
final String[] segs;
if (idx < 0) {
segs = Utils.split(string.substring(previousIndex + 1, string.length()).trim(), "=");
} else {
segs = Utils.split(string.substring(previousIndex + 1, idx).trim(), "=");
}
if (segs.length == 2) {
parameters.add(Pair.create(segs[0], segs[1]));
}
if (idx < 0) {
break;
}
previousIndex = idx;
}
return new ContentType(contentType, parameters);
}
public ContentType charset(Charset charset) {
removeParameter("charset");
return parameter("charset", charset.name());
}
private void removeParameter(String name) {
for (int i = parameters.size() - 1; i >= 0; i++) {
if (name.equals(parameters.get(i).first)) {
parameters.remove(i);
}
}
}
}

View File

@ -1,73 +0,0 @@
package org.mariotaku.simplerestapi.http;
import android.util.Pair;
import org.mariotaku.simplerestapi.RequestInfo;
import org.mariotaku.simplerestapi.Utils;
import java.util.Arrays;
import java.util.List;
/**
* Created by mariotaku on 15/2/6.
*/
public class Endpoint {
private String url;
public String getUrl() {
return url;
}
public Endpoint(String url) {
this.url = url;
}
public static String constructUrl(String endpoint, RequestInfo requestInfo) {
return constructUrl(endpoint, requestInfo.getPath(), requestInfo.getQueries());
}
public String construct(String path, List<Pair<String, String>> queries) {
return constructUrl(url, path, queries);
}
@SafeVarargs
public final String construct(String path, Pair<String, String>... queries) {
return constructUrl(url, path, Arrays.asList(queries));
}
public static String constructUrl(String endpoint, String path, List<Pair<String, String>> queries) {
if (endpoint == null) throw new NullPointerException("Endpoint is null");
final StringBuilder urlBuilder = new StringBuilder();
if (endpoint.charAt(endpoint.length() - 1) == '/') {
urlBuilder.append(endpoint.substring(0, endpoint.length() - 1));
} else {
urlBuilder.append(endpoint);
}
if (path != null) {
if (path.charAt(0) != '/') {
urlBuilder.append('/');
}
urlBuilder.append(path);
}
return constructUrl(urlBuilder.toString(), queries);
}
public static String constructUrl(String url, List<Pair<String, String>> queries) {
if (queries == null || queries.isEmpty()) return url;
final StringBuilder urlBuilder = new StringBuilder(url);
for (int i = 0, j = queries.size(); i < j; i++) {
final Pair<String, String> item = queries.get(i);
urlBuilder.append(i != 0 ? '&' : '?');
urlBuilder.append(Utils.encode(item.first, "UTF-8"));
urlBuilder.append('=');
urlBuilder.append(Utils.encode(item.second, "UTF-8"));
}
return urlBuilder.toString();
}
@SafeVarargs
public static String constructUrl(String url, Pair<String, String>... queries) {
return constructUrl(url, Arrays.asList(queries));
}
}

View File

@ -1,15 +0,0 @@
package org.mariotaku.simplerestapi.http;
import android.support.annotation.NonNull;
import java.io.IOException;
/**
* Created by mariotaku on 15/2/7.
*/
public interface RestHttpClient {
@NonNull
RestHttpResponse execute(RestHttpRequest request) throws IOException;
}

View File

@ -1,120 +0,0 @@
package org.mariotaku.simplerestapi.http;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Pair;
import org.mariotaku.simplerestapi.RequestInfo;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import java.util.ArrayList;
import java.util.List;
/**
* Created by mariotaku on 15/2/7.
*/
public final class RestHttpRequest {
private final String method;
private final String url;
private final List<Pair<String, String>> headers;
private final TypedData body;
private final Object extra;
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public List<Pair<String, String>> getHeaders() {
return headers;
}
public TypedData getBody() {
return body;
}
public Object getExtra() {
return extra;
}
@Override
public String toString() {
return "RestRequest{" +
"method='" + method + '\'' +
", url='" + url + '\'' +
", headers=" + headers +
", body=" + body +
'}';
}
public RestHttpRequest(String method, String url, List<Pair<String, String>> headers, TypedData body, Object extra) {
this.method = method;
this.url = url;
this.headers = headers;
this.body = body;
this.extra = extra;
}
public static final class Builder {
private String method;
private String url;
private List<Pair<String, String>> headers;
private TypedData body;
private Object extra;
public Builder() {
}
public Builder method(String method) {
this.method = method;
return this;
}
public Builder url(String url) {
this.url = url;
return this;
}
public Builder headers(List<Pair<String, String>> headers) {
this.headers = headers;
return this;
}
public Builder body(TypedData body) {
this.body = body;
return this;
}
public Builder extra(Object extra) {
this.extra = extra;
return this;
}
public RestHttpRequest build() {
return new RestHttpRequest(method, url, headers, body, extra);
}
}
public interface Factory {
RestHttpRequest create(@NonNull Endpoint endpoint, @NonNull RequestInfo info, @Nullable Authorization authorization);
}
public static final class DefaultFactory implements Factory {
@Override
public RestHttpRequest create(@NonNull Endpoint endpoint, @NonNull RequestInfo requestInfo, @Nullable Authorization authorization) {
final String url = Endpoint.constructUrl(endpoint.getUrl(), requestInfo);
final ArrayList<Pair<String, String>> headers = new ArrayList<>(requestInfo.getHeaders());
if (authorization != null && authorization.hasAuthorization()) {
headers.add(Pair.create("Authorization", authorization.getHeader(endpoint, requestInfo)));
}
return new RestHttpRequest(requestInfo.getMethod(), url, headers, requestInfo.getBody(), null);
}
}
}

View File

@ -1,53 +0,0 @@
package org.mariotaku.simplerestapi.http;
import android.util.Pair;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
/**
* Created by mariotaku on 15/2/7.
*/
public abstract class RestHttpResponse implements Closeable {
public abstract int getStatus();
public abstract List<Pair<String, String>> getHeaders();
public abstract TypedData getBody();
public String getHeader(String name) {
if (name == null) throw new NullPointerException();
final List<Pair<String, String>> headers = getHeaders();
if (headers == null) return null;
for (Pair<String, String> header : headers) {
if (header.first.equalsIgnoreCase(name)) return header.second;
}
return null;
}
public String[] getHeaders(String name) {
if (name == null) throw new NullPointerException();
final List<Pair<String, String>> headers = getHeaders();
if (headers == null) return new String[0];
final ArrayList<String> result = new ArrayList<>();
for (Pair<String, String> header : headers) {
if (name.equalsIgnoreCase(header.first)) {
result.add(header.second);
}
}
return result.toArray(new String[result.size()]);
}
/**
* Returns true if the code is in [200..300), which means the request was
* successfully received, understood, and accepted.
*/
public boolean isSuccessful() {
final int status = getStatus();
return status >= 200 && status < 300;
}
}

View File

@ -1,62 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi.http;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Created by mariotaku on 15/5/8.
*/
public class SimpleValueMap implements ValueMap {
private final Map<String, Object> internalMap = new HashMap<>();
@Override
public boolean has(String key) {
return internalMap.containsKey(key);
}
@Override
public Object get(String key) {
return internalMap.get(key);
}
protected void put(String key, Object value) {
internalMap.put(key, value);
}
protected void remove(String key) {
internalMap.remove(key);
}
@Override
public String[] keys() {
final Set<String> keySet = internalMap.keySet();
return keySet.toArray(new String[keySet.size()]);
}
protected void copyValue(ValueMap from, String key) {
if (from.has(key)) {
put(key, from.get(key));
}
}
}

View File

@ -1,14 +0,0 @@
package org.mariotaku.simplerestapi.http;
/**
* Created by mariotaku on 15/2/6.
*/
public interface ValueMap {
boolean has(String key);
Object get(String key);
String[] keys();
}

View File

@ -1,95 +0,0 @@
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import org.mariotaku.simplerestapi.http.ContentType;
import org.mariotaku.simplerestapi.io.StreamingGZIPInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.Charset;
/**
* Created by mariotaku on 15/2/7.
*/
public class BaseTypedData implements TypedData {
private final ContentType contentType;
private final long contentLength;
private final InputStream stream;
private final String contentEncoding;
public BaseTypedData(ContentType contentType, String contentEncoding, long contentLength, InputStream stream) throws IOException {
this.contentType = contentType;
this.contentEncoding = contentEncoding;
this.contentLength = contentLength;
if ("gzip".equals(contentEncoding)) {
this.stream = new StreamingGZIPInputStream(stream);
} else {
this.stream = stream;
}
}
@Override
public ContentType contentType() {
return contentType;
}
@Override
public String contentEncoding() {
return contentEncoding;
}
@Override
public long length() {
return contentLength;
}
@Override
public String toString() {
return "BaseTypedData{" +
"contentType=" + contentType +
", contentLength=" + contentLength +
", stream=" + stream +
", contentEncoding='" + contentEncoding + '\'' +
'}';
}
@Override
public void writeTo(@NonNull OutputStream os) throws IOException {
final byte[] buffer = new byte[8192];
for (int len; (len = stream.read(buffer)) != -1; ) {
os.write(buffer, 0, len);
}
}
@NonNull
@Override
public InputStream stream() {
return stream;
}
@Override
public void close() throws IOException {
stream.close();
}
public static TypedData wrap(Object value) {
if (value instanceof TypedData) {
return (TypedData) value;
} else if (value instanceof java.io.File) {
return new FileTypedData((java.io.File) value);
}
throw new UnsupportedOperationException();
}
public static Reader reader(TypedData data) throws IOException {
final ContentType contentType = data.contentType();
final Charset charset = contentType != null ? contentType.getCharset() : null;
return new InputStreamReader(data.stream(), charset != null ? charset : Charset.defaultCharset());
}
}

View File

@ -1,103 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ContentType;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by mariotaku on 15/5/6.
*/
public class FileTypedData implements TypedData {
private long length = -1;
private File file;
private ContentType contentType;
private String fileName;
private InputStream stream;
public FileTypedData(File file, ContentType contentType) {
this.file = file;
this.contentType = contentType;
}
public FileTypedData(InputStream stream, String fileName, long length, ContentType contentType) {
this.stream = stream;
this.fileName = fileName;
this.length = length;
this.contentType = contentType;
}
public FileTypedData(File file) {
this(file, null);
}
@Override
public long length() {
if (length != -1) return length;
if (file == null) return -1;
return length = file.length();
}
@Override
public void writeTo(@NonNull OutputStream os) throws IOException {
Utils.copyStream(stream(), os);
}
@NonNull
@Override
public InputStream stream() throws IOException {
if (stream != null) return stream;
return stream = new FileInputStream(file);
}
@Override
public void close() throws IOException {
if (stream != null) {
stream.close();
}
}
@Override
public ContentType contentType() {
if (contentType == null) {
return ContentType.OCTET_STREAM;
}
return contentType;
}
@Override
public String contentEncoding() {
return null;
}
public String fileName() {
if (fileName != null) return fileName;
return fileName = file.getName();
}
}

View File

@ -1,79 +0,0 @@
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import android.util.Pair;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ContentType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.List;
/**
* Created by mariotaku on 15/2/6.
*/
public class FormTypedBody implements TypedData {
private final List<Pair<String, String>> forms;
private final Charset charset;
public FormTypedBody(List<Pair<String, String>> forms) {
this.forms = forms;
this.charset = Charset.forName("UTF-8");
}
@Override
public ContentType contentType() {
return new ContentType("application/x-www-form-urlencoded", charset);
}
@Override
public String contentEncoding() {
return null;
}
private byte[] bytes;
private void toRawBytes() {
if (bytes != null) return;
final StringBuilder sb = new StringBuilder();
for (int i = 0, j = forms.size(); i < j; i++) {
if (i != 0) {
sb.append('&');
}
final Pair<String, String> form = forms.get(i);
sb.append(Utils.encode(form.first, charset.name()));
sb.append('=');
sb.append(Utils.encode(form.second, charset.name()));
}
bytes = sb.toString().getBytes(charset);
}
@Override
public long length() {
toRawBytes();
return bytes.length;
}
@Override
public void writeTo(@NonNull OutputStream os) throws IOException {
toRawBytes();
os.write(bytes);
}
@NonNull
@Override
public InputStream stream() {
toRawBytes();
return new ByteArrayInputStream(bytes);
}
@Override
public void close() throws IOException {
// No-op
}
}

View File

@ -1,142 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import android.util.Pair;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ContentType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Created by mariotaku on 15/5/5.
*/
public class MultipartTypedBody implements TypedData {
private final List<Pair<String, TypedData>> parts;
private final ContentType contentType;
private static final byte[] COLONSPACE = {':', ' '};
private static final byte[] CRLF = {'\r', '\n'};
private static final byte[] DASHDASH = {'-', '-'};
private final String boundary;
private boolean lengthSet;
private long length;
public MultipartTypedBody(List<Pair<String, TypedData>> parts) {
this.parts = parts;
this.contentType = ContentType.parse("multipart/form-data");
boundary = Utils.bytesToHex(UUID.randomUUID().toString().getBytes());
contentType.addParameter("boundary", boundary);
}
public MultipartTypedBody() {
this(new ArrayList<Pair<String, TypedData>>());
}
public void add(@NonNull String name, @NonNull TypedData data) {
parts.add(Pair.create(name, data));
}
@Override
public ContentType contentType() {
return contentType;
}
@Override
public String contentEncoding() {
return null;
}
@Override
public long length() throws IOException {
if (!lengthSet) {
length = 0;
for (Pair<String, TypedData> part : parts) {
length += part.second.length();
}
lengthSet = true;
}
return length;
}
@Override
public void writeTo(@NonNull OutputStream os) throws IOException {
for (Pair<String, TypedData> part : parts) {
os.write(DASHDASH);
os.write(boundary.getBytes());
os.write(CRLF);
final ContentType contentDisposition = new ContentType("form-data").parameter("name", part.first);
final ContentType contentType = part.second.contentType();
final long contentLength = part.second.length();
if (part.second instanceof FileTypedData) {
contentDisposition.addParameter("filename", ((FileTypedData) part.second).fileName());
}
os.write("Content-Disposition".getBytes());
os.write(COLONSPACE);
os.write(contentDisposition.toHeader().getBytes());
os.write(CRLF);
if (contentType != null) {
os.write("Content-Type".getBytes());
os.write(COLONSPACE);
os.write(contentType.toHeader().getBytes());
os.write(CRLF);
}
if (contentLength != -1) {
os.write("Content-Length".getBytes());
os.write(COLONSPACE);
os.write(String.valueOf(contentLength).getBytes());
os.write(CRLF);
}
os.write(CRLF);
part.second.writeTo(os);
os.write(CRLF);
}
os.write(DASHDASH);
os.write(boundary.getBytes());
os.write(DASHDASH);
os.write(CRLF);
}
@NonNull
@Override
public InputStream stream() throws IOException {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
writeTo(os);
return new ByteArrayInputStream(os.toByteArray());
}
@Override
public void close() throws IOException {
for (Pair<String, TypedData> part : parts) {
part.second.close();
}
}
}

View File

@ -1,80 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ContentType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* Created by mariotaku on 15/5/12.
*/
public class StringTypedData implements TypedData {
private final ContentType contentType;
private final byte[] data;
private ByteArrayInputStream is;
public StringTypedData(String string, Charset charset) {
this.contentType = ContentType.parse("text/plain").charset(charset);
this.data = string.getBytes(charset);
}
@Nullable
@Override
public ContentType contentType() {
return contentType;
}
@Override
public String contentEncoding() {
return null;
}
@Override
public long length() throws IOException {
return data.length;
}
@Override
public void writeTo(@NonNull OutputStream os) throws IOException {
os.write(data);
}
@NonNull
@Override
public InputStream stream() throws IOException {
if (is != null) return is;
return is = new ByteArrayInputStream(data);
}
@Override
public void close() throws IOException {
Utils.closeSilently(is);
}
}

View File

@ -1,29 +0,0 @@
package org.mariotaku.simplerestapi.http.mime;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.mariotaku.simplerestapi.http.ContentType;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by mariotaku on 15/2/6.
*/
public interface TypedData {
@Nullable
ContentType contentType();
String contentEncoding();
long length() throws IOException;
void writeTo(@NonNull OutputStream os) throws IOException;
@NonNull
InputStream stream() throws IOException;
void close() throws IOException;
}

View File

@ -1,29 +0,0 @@
package org.mariotaku.simplerestapi.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
public final class StreamingGZIPInputStream extends GZIPInputStream {
private final InputStream wrapped;
public StreamingGZIPInputStream(InputStream is) throws IOException {
super(is);
wrapped = is;
}
/**
* Overrides behavior of GZIPInputStream which assumes we have all the data available
* which is not true for streaming. We instead rely on the underlying stream to tell us
* how much data is available.
* <p/>
* Programs should not count on this method to return the actual number
* of bytes that could be read without blocking.
*
* @return - whatever the wrapped InputStream returns
* @throws IOException if an I/O error occurs.
*/
public int available() throws IOException {
return wrapped.available();
}
}

View File

@ -1,20 +0,0 @@
package org.mariotaku.simplerestapi.method;
import org.mariotaku.simplerestapi.RestMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/7.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@RestMethod(value = "GET", hasBody = false)
public @interface GET {
String METHOD = "GET";
String value();
}

View File

@ -1,20 +0,0 @@
package org.mariotaku.simplerestapi.method;
import org.mariotaku.simplerestapi.RestMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/7.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@RestMethod(value = "POST", hasBody = true)
public @interface POST {
String METHOD = "POST";
String value();
}

View File

@ -1,19 +0,0 @@
package org.mariotaku.simplerestapi.param;
import org.mariotaku.simplerestapi.http.BodyType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Body {
BodyType value();
String encoding() default "";
}

View File

@ -1,15 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Extra {
String[] value();
}

View File

@ -1,17 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface File {
String contentType() default "";
String encoding() default "";
}

View File

@ -1,17 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Form {
String[] value() default {};
char arrayDelimiter() default ',';
}

View File

@ -1,16 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Header {
String[] value();
char arrayDelimiter() default ',';
}

View File

@ -1,16 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Part {
String[] value();
char arrayDelimiter() default ',';
}

View File

@ -1,17 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Path {
String value();
boolean encoded() default false;
}

View File

@ -1,16 +0,0 @@
package org.mariotaku.simplerestapi.param;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by mariotaku on 15/2/6.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
String[] value() default {};
char arrayDelimiter() default ',';
}

View File

@ -22,18 +22,17 @@ package org.mariotaku.twidere.api.twitter;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.simplerestapi.http.RestHttpRequest;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpRequest;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.http.HttpResponseCode;
import org.mariotaku.twidere.api.twitter.model.ErrorInfo;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;
import org.mariotaku.twidere.api.twitter.model.TwitterResponse;
import org.mariotaku.twidere.api.twitter.model.impl.RateLimitStatusJSONImpl;
import org.mariotaku.twidere.api.twitter.util.InternalParseUtil;
import java.util.Locale;
import org.mariotaku.twidere.api.twitter.http.HttpResponseCode;
import org.mariotaku.twidere.api.twitter.util.InternalParseUtil;
/**
* An exception class that will be thrown when TwitterAPI calls are failed.<br>
* In case the Twitter server returned HTTP error code, you can get the HTTP
@ -84,13 +83,16 @@ public class TwitterException extends Exception implements TwitterResponse, Http
this(message);
setResponse(res);
request = req;
statusCode = res != null ? res.getStatus() : -1;
}
public void setResponse(RestHttpResponse res) {
response = res;
if (res != null) {
rateLimitStatus = RateLimitStatusJSONImpl.createFromResponseHeader(res);
statusCode = res.getStatus();
} else {
rateLimitStatus = null;
statusCode = -1;
}
}

View File

@ -20,11 +20,11 @@
package org.mariotaku.twidere.api.twitter;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Extra;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Extra;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
/**

View File

@ -19,16 +19,15 @@
package org.mariotaku.twidere.api.twitter;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.http.mime.FileTypedData;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Part;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Part;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.restfu.http.mime.FileTypedData;
import java.io.File;
import org.mariotaku.twidere.api.twitter.model.MediaUploadResponse;
import org.mariotaku.twidere.api.twitter.TwitterException;
@SuppressWarnings("RedundantThrows")
public interface TwitterUpload {

View File

@ -0,0 +1,32 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter;
import org.mariotaku.restfu.annotation.method.GET;
/**
* Created by mariotaku on 15/5/26.
*/
public interface TwitterUserStream {
@GET("/user.json")
void getUserStream(UserStreamCallback callback);
}

View File

@ -0,0 +1,185 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter;
import com.bluelinelabs.logansquare.LoganSquare;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.mariotaku.restfu.callback.RawCallback;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.StatusDeletionNotice;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.api.twitter.model.UserList;
import org.mariotaku.twidere.api.twitter.model.Warning;
import org.mariotaku.twidere.api.twitter.util.CRLFLineReader;
import org.mariotaku.twidere.api.twitter.util.JSONObjectType;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Created by mariotaku on 15/5/26.
*/
public abstract class UserStreamCallback implements RawCallback {
private boolean disconnected;
@Override
public final void result(final RestHttpResponse response) throws IOException {
if (!response.isSuccessful()) {
final TwitterException cause = new TwitterException();
cause.setResponse(response);
onException(cause);
return;
}
final ObjectMapper mapper = new ObjectMapper(LoganSquare.JSON_FACTORY);
final CRLFLineReader reader = new CRLFLineReader(new InputStreamReader(response.getBody().stream(), "UTF-8"));
for (String line; (line = reader.readLine()) != null && !disconnected; ) {
if (line.isEmpty()) continue;
JsonNode rootNode = mapper.readTree(line);
switch (JSONObjectType.determine(rootNode)) {
case SENDER: {
break;
}
case STATUS: {
onStatus(LoganSquare.mapperFor(Status.class).parse(rootNode.traverse()));
break;
}
case DIRECT_MESSAGE: {
onDirectMessage(LoganSquare.mapperFor(DirectMessage.class).parse(rootNode.traverse()));
break;
}
case DELETE: {
break;
}
case LIMIT:
break;
case STALL_WARNING:
break;
case SCRUB_GEO:
break;
case FRIENDS:
break;
case FAVORITE: {
onFavorite(parse(User.class, rootNode.get("source")),
parse(User.class, rootNode.get("target")),
parse(Status.class, rootNode.get("target_object")));
break;
}
case UNFAVORITE:
break;
case FOLLOW:
break;
case UNFOLLOW:
break;
case USER_LIST_MEMBER_ADDED:
break;
case USER_LIST_MEMBER_DELETED:
break;
case USER_LIST_SUBSCRIBED:
break;
case USER_LIST_UNSUBSCRIBED:
break;
case USER_LIST_CREATED:
break;
case USER_LIST_UPDATED:
break;
case USER_LIST_DESTROYED:
break;
case USER_UPDATE:
break;
case USER_DELETE:
break;
case USER_SUSPEND:
break;
case BLOCK:
break;
case UNBLOCK:
break;
case DISCONNECTION:
break;
case UNKNOWN:
break;
}
}
reader.close();
response.close();
}
private static <T> T parse(final Class<T> cls, final JsonNode json) throws IOException {
return LoganSquare.mapperFor(cls).parse(json.traverse());
}
@Override
public final void error(final Throwable cause) {
onException(cause);
}
public void disconnect() {
disconnected = true;
}
public abstract void onBlock(User source, User blockedUser);
public abstract void onDeletionNotice(long directMessageId, long userId);
public abstract void onDeletionNotice(StatusDeletionNotice statusDeletionNotice);
public abstract void onDirectMessage(DirectMessage directMessage);
public abstract void onException(Throwable ex);
public abstract void onFavorite(User source, User target, Status favoritedStatus);
public abstract void onFollow(User source, User followedUser);
public abstract void onFriendList(long[] friendIds);
public abstract void onScrubGeo(long userId, long upToStatusId);
public abstract void onStallWarning(Warning warn);
public abstract void onStatus(Status status);
public abstract void onTrackLimitationNotice(int numberOfLimitedStatuses);
public abstract void onUnblock(User source, User unblockedUser);
public abstract void onUnfavorite(User source, User target, Status unfavoritedStatus);
public abstract void onUserListCreation(User listOwner, UserList list);
public abstract void onUserListDeletion(User listOwner, UserList list);
public abstract void onUserListMemberAddition(User addedMember, User listOwner, UserList list);
public abstract void onUserListMemberDeletion(User deletedMember, User listOwner, UserList list);
public abstract void onUserListSubscription(User subscriber, User listOwner, UserList list);
public abstract void onUserListUnsubscription(User subscriber, User listOwner, UserList list);
public abstract void onUserListUpdate(User listOwner, UserList list);
public abstract void onUserProfileUpdate(User updatedUser);
}

View File

@ -19,12 +19,12 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
import org.mariotaku.twidere.api.twitter.model.Paging;

View File

@ -19,17 +19,16 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.TwitterException;
/**
* @author Joern Huxhorn - jhuxhorn at googlemail.com

View File

@ -19,12 +19,12 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Friendship;
import org.mariotaku.twidere.api.twitter.model.FriendshipUpdate;

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Language;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;

View File

@ -19,12 +19,12 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.PageableResponseList;
import org.mariotaku.twidere.api.twitter.model.Paging;

View File

@ -19,8 +19,8 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Activity;
import org.mariotaku.twidere.api.twitter.model.Paging;

View File

@ -19,10 +19,10 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Path;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Path;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
@SuppressWarnings("RedundantThrows")

View File

@ -19,8 +19,8 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.ResponseList;

View File

@ -19,9 +19,9 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Path;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Path;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.ResponseList;

View File

@ -19,12 +19,12 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Path;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Path;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.SavedSearch;

View File

@ -19,12 +19,11 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.QueryResult;
import org.mariotaku.twidere.api.twitter.model.SearchQuery;
import org.mariotaku.twidere.api.twitter.TwitterException;
@SuppressWarnings("RedundantThrows")
public interface SearchResource {

View File

@ -19,10 +19,10 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.User;

View File

@ -19,9 +19,9 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.model.Paging;
import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.Status;

View File

@ -19,8 +19,8 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.GeoLocation;
import org.mariotaku.twidere.api.twitter.model.Location;

View File

@ -19,13 +19,13 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Path;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Path;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.IDs;
import org.mariotaku.twidere.api.twitter.model.Paging;

View File

@ -19,14 +19,14 @@
package org.mariotaku.twidere.api.twitter.api;
import org.mariotaku.simplerestapi.http.BodyType;
import org.mariotaku.simplerestapi.http.mime.FileTypedData;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.simplerestapi.method.POST;
import org.mariotaku.simplerestapi.param.Body;
import org.mariotaku.simplerestapi.param.Form;
import org.mariotaku.simplerestapi.param.Part;
import org.mariotaku.simplerestapi.param.Query;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.method.POST;
import org.mariotaku.restfu.annotation.param.Body;
import org.mariotaku.restfu.annotation.param.Form;
import org.mariotaku.restfu.annotation.param.Part;
import org.mariotaku.restfu.annotation.param.Query;
import org.mariotaku.restfu.http.BodyType;
import org.mariotaku.restfu.http.mime.FileTypedData;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.model.AccountSettings;
import org.mariotaku.twidere.api.twitter.model.Category;

View File

@ -21,9 +21,9 @@ package org.mariotaku.twidere.api.twitter.auth;
import android.util.Base64;
import org.mariotaku.simplerestapi.RequestInfo;
import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.restfu.RestRequestInfo;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
/**
* Created by mariotaku on 15/4/19.
@ -39,7 +39,7 @@ public final class BasicAuthorization implements Authorization {
}
@Override
public String getHeader(Endpoint endpoint, RequestInfo info) {
public String getHeader(Endpoint endpoint, RestRequestInfo info) {
if (!hasAuthorization()) return null;
return "Basic " + Base64.encodeToString((user + ":" + password).getBytes(), Base64.NO_WRAP);
}

View File

@ -19,9 +19,9 @@
package org.mariotaku.twidere.api.twitter.auth;
import org.mariotaku.simplerestapi.RequestInfo;
import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.restfu.RestRequestInfo;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
/**
* Created by mariotaku on 15/4/19.
@ -29,7 +29,7 @@ import org.mariotaku.simplerestapi.http.Endpoint;
public final class EmptyAuthorization implements Authorization {
@Override
public String getHeader(Endpoint endpoint, RequestInfo info) {
public String getHeader(Endpoint endpoint, RestRequestInfo info) {
return null;
}

View File

@ -22,10 +22,10 @@ package org.mariotaku.twidere.api.twitter.auth;
import android.util.Base64;
import android.util.Pair;
import org.mariotaku.simplerestapi.RequestInfo;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.Authorization;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.restfu.RestRequestInfo;
import org.mariotaku.restfu.Utils;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
@ -130,7 +130,7 @@ public class OAuthAuthorization implements Authorization, OAuthSupport {
}
@Override
public String getHeader(Endpoint endpoint, RequestInfo request) {
public String getHeader(Endpoint endpoint, RestRequestInfo request) {
if (!(endpoint instanceof OAuthEndpoint))
throw new IllegalArgumentException("OAuthEndpoint required");
final OAuthEndpoint oauthEndpoint = (OAuthEndpoint) endpoint;

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.auth;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.restfu.http.Endpoint;
/**
* Created by mariotaku on 15/2/6.

View File

@ -19,9 +19,10 @@
package org.mariotaku.twidere.api.twitter.auth;
import org.apache.commons.lang3.tuple.Pair;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ValueMap;
import android.util.Pair;
import org.mariotaku.restfu.Utils;
import org.mariotaku.restfu.http.ValueMap;
import java.nio.charset.Charset;
import java.text.ParseException;
@ -63,21 +64,21 @@ public class OAuthToken implements ValueMap {
List<Pair<String, String>> params = new ArrayList<>();
Utils.parseGetParameters(body, params, charset.name());
for (Pair<String, String> param : params) {
switch (param.getKey()) {
switch (param.first) {
case "oauth_token": {
oauthToken = param.getValue();
oauthToken = param.second;
break;
}
case "oauth_token_secret": {
oauthTokenSecret = param.getValue();
oauthTokenSecret = param.second;
break;
}
case "user_id": {
userId = Long.parseLong(param.getValue());
userId = Long.parseLong(param.second);
break;
}
case "screen_name": {
screenName = param.getValue();
screenName = param.second;
break;
}
}

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* Created by mariotaku on 15/5/13.

View File

@ -24,7 +24,7 @@ import com.bluelinelabs.logansquare.typeconverters.TypeConverter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import org.mariotaku.simplerestapi.http.ValueMap;
import org.mariotaku.restfu.http.ValueMap;
import org.mariotaku.twidere.api.twitter.model.impl.GeoPoint;
import java.io.IOException;

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.ValueMap;
import org.mariotaku.restfu.http.ValueMap;
/**

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* Created by mariotaku on 15/2/6.

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* Created by mariotaku on 15/1/6.

View File

@ -21,7 +21,7 @@ package org.mariotaku.twidere.api.twitter.model;
import android.support.annotation.NonNull;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* A data class represents search query.<br>

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* Created by mariotaku on 15/1/6.

View File

@ -0,0 +1,27 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter.model;
/**
* Created by mariotaku on 15/5/26.
*/
public interface StatusDeletionNotice {
long getStatusId();
}

View File

@ -19,8 +19,8 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.Utils;
import org.mariotaku.restfu.http.SimpleValueMap;
public final class StatusUpdate extends SimpleValueMap {

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
/**
* Super interface of Twitter Response data interfaces which indicates that rate

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model;
import org.mariotaku.simplerestapi.http.SimpleValueMap;
import org.mariotaku.restfu.http.SimpleValueMap;
/**
* Created by mariotaku on 15/5/8.

View File

@ -0,0 +1,26 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter.model;
/**
* Created by mariotaku on 15/5/26.
*/
public interface Warning {
}

View File

@ -26,7 +26,7 @@ import com.bluelinelabs.logansquare.typeconverters.TypeConverter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.model.CardEntity;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;
import org.mariotaku.twidere.api.twitter.model.User;

View File

@ -22,7 +22,7 @@ package org.mariotaku.twidere.api.twitter.model.impl;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model.impl;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
import java.util.ArrayList;
import java.util.Collection;

View File

@ -1,5 +1,5 @@
/*
* Twidere - Twitter client for Android
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
@ -17,24 +17,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension.streaming.util;
package org.mariotaku.twidere.api.twitter.model.impl;
import android.content.Context;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.http.HttpClient;
import org.mariotaku.twidere.api.twitter.http.HttpClientConfiguration;
import org.mariotaku.twidere.api.twitter.http.HttpClientFactory;
import org.mariotaku.twidere.api.twitter.model.StatusDeletionNotice;
/**
* Created by mariotaku on 15/1/22.
* Created by mariotaku on 15/5/26.
*/
public class OkHttpClientFactory implements HttpClientFactory {
public OkHttpClientFactory(Context context) {
}
@JsonObject
public class StatusDeletionNoticeImpl implements StatusDeletionNotice {
@Override
public HttpClient getInstance(HttpClientConfiguration conf) {
return new OkHttpClientImpl(conf);
public long getStatusId() {
return 0;
}
}

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.model.impl;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;
import org.mariotaku.twidere.api.twitter.model.TwitterResponse;

View File

@ -0,0 +1,31 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter.model.impl;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.twidere.api.twitter.model.Warning;
/**
* Created by mariotaku on 15/5/26.
*/
@JsonObject
public class WarningImpl implements Warning {
}

View File

@ -0,0 +1,79 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
/**
* CRLFLineReader implements a readLine() method that requires
* exactly CRLF to terminate an input line.
* This is required for IMAP, which allows bare CR and LF.
*
* @since 3.0
*/
public final class CRLFLineReader extends BufferedReader
{
private static final char LF = '\n';
private static final char CR = '\r';
/**
* Creates a CRLFLineReader that wraps an existing Reader
* input source.
* @param reader The Reader input source.
*/
public CRLFLineReader(Reader reader)
{
super(reader);
}
/**
* Read a line of text.
* A line is considered to be terminated by carriage return followed immediately by a linefeed.
* This contrasts with BufferedReader which also allows other combinations.
* @since 3.0
*/
@Override
public String readLine() throws IOException {
StringBuilder sb = new StringBuilder();
int intch;
boolean prevWasCR = false;
synchronized(lock) { // make thread-safe (hopefully!)
while((intch = read()) != -1)
{
if (prevWasCR && intch == LF) {
return sb.substring(0, sb.length()-1);
}
if (intch == CR) {
prevWasCR = true;
} else {
prevWasCR = false;
}
sb.append((char) intch);
}
}
String string = sb.toString();
if (string.length() == 0) { // immediate EOF
return null;
}
return string;
}
}

View File

@ -19,7 +19,7 @@
package org.mariotaku.twidere.api.twitter.util;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.api.twitter.model.TwitterResponse;
/**

View File

@ -0,0 +1,132 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.api.twitter.util;
import com.fasterxml.jackson.databind.JsonNode;
/**
* @author Dan Checkoway - dcheckoway at gmail.com
* @since Twitter4J 2.1.9
*/
public final class JSONObjectType {
public enum Type {
SENDER,
STATUS,
DIRECT_MESSAGE,
DELETE,
LIMIT,
STALL_WARNING,
SCRUB_GEO,
FRIENDS,
FAVORITE,
UNFAVORITE,
FOLLOW,
UNFOLLOW,
USER_LIST_MEMBER_ADDED,
USER_LIST_MEMBER_DELETED,
USER_LIST_SUBSCRIBED,
USER_LIST_UNSUBSCRIBED,
USER_LIST_CREATED,
USER_LIST_UPDATED,
USER_LIST_DESTROYED,
USER_UPDATE,
USER_DELETE,
USER_SUSPEND,
BLOCK,
UNBLOCK,
DISCONNECTION,
UNKNOWN
}
/**
* Determine the respective object type for a given JSONObject. This
* method inspects the object to figure out what type of object it
* represents. This is useful when processing JSON events of mixed type
* from a stream, in which case you may need to know what type of object
* to construct, or how to handle the event properly.
*
* @param json the JSONObject whose type should be determined
* @return the determined JSONObjectType, or null if not recognized
*/
public static Type determine(JsonNode json) {
// This code originally lived in AbstractStreamImplementation.
// I've moved it in here to expose it as a public encapsulation of
// the object type determination logic.
if (json.hasNonNull("sender")) {
return Type.SENDER;
} else if (json.hasNonNull("text")) {
return Type.STATUS;
} else if (json.hasNonNull("direct_message")) {
return Type.DIRECT_MESSAGE;
} else if (json.hasNonNull("delete")) {
return Type.DELETE;
} else if (json.hasNonNull("limit")) {
return Type.LIMIT;
} else if (json.hasNonNull("warning")) {
return Type.STALL_WARNING;
} else if (json.hasNonNull("scrub_geo")) {
return Type.SCRUB_GEO;
} else if (json.hasNonNull("friends")) {
return Type.FRIENDS;
} else if (json.hasNonNull("event")) {
String event;
event = json.get("event").asText("event");
if ("favorite".equals(event)) {
return Type.FAVORITE;
} else if ("unfavorite".equals(event)) {
return Type.UNFAVORITE;
} else if ("follow".equals(event)) {
return Type.FOLLOW;
} else if ("unfollow".equals(event)) {
return Type.UNFOLLOW;
} else if (event.startsWith("list")) {
if ("list_member_added".equals(event)) {
return Type.USER_LIST_MEMBER_ADDED;
} else if ("list_member_removed".equals(event)) {
return Type.USER_LIST_MEMBER_DELETED;
} else if ("list_user_subscribed".equals(event)) {
return Type.USER_LIST_SUBSCRIBED;
} else if ("list_user_unsubscribed".equals(event)) {
return Type.USER_LIST_UNSUBSCRIBED;
} else if ("list_created".equals(event)) {
return Type.USER_LIST_CREATED;
} else if ("list_updated".equals(event)) {
return Type.USER_LIST_UPDATED;
} else if ("list_destroyed".equals(event)) {
return Type.USER_LIST_DESTROYED;
}
} else if ("user_update".equals(event)) {
return Type.USER_UPDATE;
} else if ("user_delete".equals(event)) {
return Type.USER_DELETE;
} else if ("user_suspend".equals(event)) {
return Type.USER_SUSPEND;
} else if ("block".equals(event)) {
return Type.BLOCK;
} else if ("unblock".equals(event)) {
return Type.UNBLOCK;
}
} else if (json.hasNonNull("disconnect")) {
return Type.DISCONNECTION;
}
return Type.UNKNOWN;
}
}

View File

@ -25,11 +25,11 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import org.mariotaku.simplerestapi.Converter;
import org.mariotaku.simplerestapi.Utils;
import org.mariotaku.simplerestapi.http.ContentType;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.simplerestapi.http.mime.TypedData;
import org.mariotaku.restfu.Converter;
import org.mariotaku.restfu.Utils;
import org.mariotaku.restfu.http.ContentType;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.restfu.http.mime.TypedData;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
import org.mariotaku.twidere.api.twitter.model.AccountSettings;
@ -52,6 +52,7 @@ import org.mariotaku.twidere.api.twitter.model.ResponseList;
import org.mariotaku.twidere.api.twitter.model.SavedSearch;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.StatusActivitySummary;
import org.mariotaku.twidere.api.twitter.model.StatusDeletionNotice;
import org.mariotaku.twidere.api.twitter.model.TimeZone;
import org.mariotaku.twidere.api.twitter.model.TranslationResult;
import org.mariotaku.twidere.api.twitter.model.Trend;
@ -60,6 +61,7 @@ import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.api.twitter.model.UserList;
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
import org.mariotaku.twidere.api.twitter.model.Warning;
import org.mariotaku.twidere.api.twitter.model.impl.AccountSettingsImpl;
import org.mariotaku.twidere.api.twitter.model.impl.ActivityImpl;
import org.mariotaku.twidere.api.twitter.model.impl.CardEntityImpl;
@ -80,6 +82,7 @@ import org.mariotaku.twidere.api.twitter.model.impl.RelationshipWrapper;
import org.mariotaku.twidere.api.twitter.model.impl.ResponseListImpl;
import org.mariotaku.twidere.api.twitter.model.impl.SavedSearchImpl;
import org.mariotaku.twidere.api.twitter.model.impl.StatusActivitySummaryImpl;
import org.mariotaku.twidere.api.twitter.model.impl.StatusDeletionNoticeImpl;
import org.mariotaku.twidere.api.twitter.model.impl.StatusImpl;
import org.mariotaku.twidere.api.twitter.model.impl.TimeZoneImpl;
import org.mariotaku.twidere.api.twitter.model.impl.TranslationResultImpl;
@ -91,6 +94,7 @@ import org.mariotaku.twidere.api.twitter.model.impl.UrlEntityImpl;
import org.mariotaku.twidere.api.twitter.model.impl.UserImpl;
import org.mariotaku.twidere.api.twitter.model.impl.UserListImpl;
import org.mariotaku.twidere.api.twitter.model.impl.UserMentionEntityImpl;
import org.mariotaku.twidere.api.twitter.model.impl.WarningImpl;
import org.mariotaku.twidere.api.twitter.model.impl.Wrapper;
import java.io.ByteArrayOutputStream;
@ -145,6 +149,8 @@ public class TwitterConverter implements Converter {
TypeConverterMapper.register(AccountSettings.class, AccountSettingsImpl.class);
TypeConverterMapper.register(IDs.class, IDsImpl.class, IDsImpl.MAPPER);
TypeConverterMapper.register(Activity.class, ActivityImpl.class, ActivityImpl.MAPPER);
TypeConverterMapper.register(Warning.class, WarningImpl.class);
TypeConverterMapper.register(StatusDeletionNotice.class, StatusDeletionNoticeImpl.class);
LoganSquare.registerTypeConverter(Indices.class, Indices.CONVERTER);
LoganSquare.registerTypeConverter(GeoLocation.class, GeoLocation.CONVERTER);

View File

@ -21,7 +21,7 @@ package org.mariotaku.twidere.constant;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.Preference;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.model.ParcelableCredentials;
import static org.mariotaku.twidere.annotation.Preference.Type.BOOLEAN;
import static org.mariotaku.twidere.annotation.Preference.Type.INT;
@ -240,7 +240,7 @@ public interface SharedPreferenceConstants {
String KEY_SAME_OAUTH_SIGNING_URL = "same_oauth_signing_url";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false)
String KEY_NO_VERSION_SUFFIX = "no_version_suffix";
@Preference(type = INT, hasDefault = true, defaultInt = Accounts.AUTH_TYPE_OAUTH)
@Preference(type = INT, hasDefault = true, defaultInt = ParcelableCredentials.AUTH_TYPE_OAUTH)
String KEY_AUTH_TYPE = "auth_type";
@Preference(type = STRING, hasDefault = true, defaultString = TwidereConstants.TWITTER_CONSUMER_KEY)
String KEY_CONSUMER_KEY = "consumer_key";

View File

@ -29,6 +29,8 @@ import android.support.annotation.Nullable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import org.mariotaku.querybuilder.Columns.Column;
import org.mariotaku.querybuilder.Expression;
@ -41,6 +43,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ParcelablePlease(allFields = false)
@JsonObject
public class ParcelableAccount implements Parcelable {
@ -57,28 +60,35 @@ public class ParcelableAccount implements Parcelable {
}
};
@ParcelableThisPlease
@JsonField(name = "screen_name")
public String screen_name;
@ParcelableThisPlease
@JsonField(name = "name")
public String name;
@ParcelableThisPlease
@JsonField(name = "profile_image_url")
public String profile_image_url;
@ParcelableThisPlease
@JsonField(name = "profile_banner_url")
public String profile_banner_url;
@ParcelableThisPlease
@JsonField(name = "account_id")
public long account_id;
@ParcelableThisPlease
@JsonField(name = "color")
public int color;
@ParcelableThisPlease
@JsonField(name = "is_activated")
public boolean is_activated;
@ParcelableThisPlease
@JsonField(name = "is_dummy")
public boolean is_dummy;
@ -95,14 +105,7 @@ public class ParcelableAccount implements Parcelable {
}
public ParcelableAccount(final Parcel source) {
is_dummy = source.readInt() == 1;
is_activated = source.readInt() == 1;
account_id = source.readLong();
name = source.readString();
screen_name = source.readString();
profile_image_url = source.readString();
profile_banner_url = source.readString();
color = source.readInt();
ParcelableAccountParcelablePlease.readFromParcel(this, source);
}
public ParcelableAccount() {
@ -116,14 +119,7 @@ public class ParcelableAccount implements Parcelable {
@Override
public void writeToParcel(final Parcel out, final int flags) {
out.writeInt(is_dummy ? 1 : 0);
out.writeInt(is_activated ? 1 : 0);
out.writeLong(account_id);
out.writeString(name);
out.writeString(screen_name);
out.writeString(profile_image_url);
out.writeString(profile_banner_url);
out.writeInt(color);
ParcelableAccountParcelablePlease.writeToParcel(this, out, flags);
}
public static ParcelableAccount dummyAccount() {
@ -287,109 +283,6 @@ public class ParcelableAccount implements Parcelable {
+ ", is_activated=" + is_activated + ", is_dummy=" + is_dummy + "}";
}
@JsonObject
public static class ParcelableCredentials extends ParcelableAccount {
public static final Parcelable.Creator<ParcelableCredentials> CREATOR = new Parcelable.Creator<ParcelableCredentials>() {
@Override
public ParcelableCredentials createFromParcel(final Parcel in) {
return new ParcelableCredentials(in);
}
@Override
public ParcelableCredentials[] newArray(final int size) {
return new ParcelableCredentials[size];
}
};
@JsonField(name = "auth_type")
public int auth_type;
@JsonField(name = "consumer_key")
public String consumer_key;
@JsonField(name = "consumer_secret")
public String consumer_secret;
@JsonField(name = "basic_auth_username")
public String basic_auth_username;
@JsonField(name = "basic_auth_password")
public String basic_auth_password;
@JsonField(name = "oauth_token")
public String oauth_token;
@JsonField(name = "oauth_token_secret")
public String oauth_token_secret;
@JsonField(name = "api_url_format")
public String api_url_format;
@JsonField(name = "same_oauth_signing_url")
public boolean same_oauth_signing_url;
@JsonField(name = "no_version_suffix")
public boolean no_version_suffix;
public ParcelableCredentials() {
}
public ParcelableCredentials(final Cursor cursor, final Indices indices) {
super(cursor, indices);
auth_type = cursor.getInt(indices.auth_type);
consumer_key = cursor.getString(indices.consumer_key);
consumer_secret = cursor.getString(indices.consumer_secret);
basic_auth_username = cursor.getString(indices.basic_auth_username);
basic_auth_password = cursor.getString(indices.basic_auth_password);
oauth_token = cursor.getString(indices.oauth_token);
oauth_token_secret = cursor.getString(indices.oauth_token_secret);
api_url_format = cursor.getString(indices.api_url_format);
same_oauth_signing_url = cursor.getInt(indices.same_oauth_signing_url) == 1;
no_version_suffix = cursor.getInt(indices.no_version_suffix) == 1;
}
public ParcelableCredentials(Parcel in) {
super(in);
auth_type = in.readInt();
consumer_key = in.readString();
consumer_secret = in.readString();
basic_auth_username = in.readString();
basic_auth_password = in.readString();
oauth_token = in.readString();
oauth_token_secret = in.readString();
api_url_format = in.readString();
same_oauth_signing_url = in.readInt() == 1;
no_version_suffix = in.readInt() == 1;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(auth_type);
out.writeString(consumer_key);
out.writeString(consumer_secret);
out.writeString(basic_auth_username);
out.writeString(basic_auth_password);
out.writeString(oauth_token);
out.writeString(oauth_token_secret);
out.writeString(api_url_format);
out.writeInt(same_oauth_signing_url ? 1 : 0);
out.writeInt(no_version_suffix ? 1 : 0);
}
@Override
public String toString() {
return "AccountWithCredentials{auth_type=" + auth_type + ", consumer_key=" + consumer_key
+ ", consumer_secret=" + consumer_secret + ", basic_auth_password=" + basic_auth_password
+ ", oauth_token=" + oauth_token + ", oauth_token_secret=" + oauth_token_secret
+ ", api_url_format=" + api_url_format + ", same_oauth_signing_url=" + same_oauth_signing_url + "}";
}
}
public static final class Indices {
public final int screen_name, name, account_id, profile_image_url, profile_banner_url, color, is_activated,

View File

@ -0,0 +1,124 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.model;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
/**
* Created by mariotaku on 15/5/26.
*/
@JsonObject
@ParcelablePlease(allFields = false)
public class ParcelableCredentials extends ParcelableAccount {
public static final Creator<org.mariotaku.twidere.model.ParcelableCredentials> CREATOR = new Creator<org.mariotaku.twidere.model.ParcelableCredentials>() {
@Override
public ParcelableCredentials createFromParcel(final Parcel in) {
return new ParcelableCredentials(in);
}
@Override
public ParcelableCredentials[] newArray(final int size) {
return new ParcelableCredentials[size];
}
};
public static final int AUTH_TYPE_OAUTH = 0;
public static final int AUTH_TYPE_XAUTH = 1;
public static final int AUTH_TYPE_BASIC = 2;
public static final int AUTH_TYPE_TWIP_O_MODE = 3;
@ParcelableThisPlease
@JsonField(name = "auth_type")
public int auth_type;
@ParcelableThisPlease
@JsonField(name = "consumer_key")
public String consumer_key;
@ParcelableThisPlease
@JsonField(name = "consumer_secret")
public String consumer_secret;
@ParcelableThisPlease
@JsonField(name = "basic_auth_username")
public String basic_auth_username;
@ParcelableThisPlease
@JsonField(name = "basic_auth_password")
public String basic_auth_password;
@ParcelableThisPlease
@JsonField(name = "oauth_token")
public String oauth_token;
@ParcelableThisPlease
@JsonField(name = "oauth_token_secret")
public String oauth_token_secret;
@ParcelableThisPlease
@JsonField(name = "api_url_format")
public String api_url_format;
@ParcelableThisPlease
@JsonField(name = "same_oauth_signing_url")
public boolean same_oauth_signing_url;
@ParcelableThisPlease
@JsonField(name = "no_version_suffix")
public boolean no_version_suffix;
public ParcelableCredentials() {
}
public ParcelableCredentials(final Cursor cursor, final Indices indices) {
super(cursor, indices);
auth_type = cursor.getInt(indices.auth_type);
consumer_key = cursor.getString(indices.consumer_key);
consumer_secret = cursor.getString(indices.consumer_secret);
basic_auth_username = cursor.getString(indices.basic_auth_username);
basic_auth_password = cursor.getString(indices.basic_auth_password);
oauth_token = cursor.getString(indices.oauth_token);
oauth_token_secret = cursor.getString(indices.oauth_token_secret);
api_url_format = cursor.getString(indices.api_url_format);
same_oauth_signing_url = cursor.getInt(indices.same_oauth_signing_url) == 1;
no_version_suffix = cursor.getInt(indices.no_version_suffix) == 1;
}
public ParcelableCredentials(Parcel in) {
super(in);
ParcelableCredentialsParcelablePlease.readFromParcel(this, in);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
ParcelableCredentialsParcelablePlease.writeToParcel(this, out, flags);
}
@Override
public String toString() {
return "AccountWithCredentials{auth_type=" + auth_type + ", consumer_key=" + consumer_key
+ ", consumer_secret=" + consumer_secret + ", basic_auth_password=" + basic_auth_password
+ ", oauth_token=" + oauth_token + ", oauth_token_secret=" + oauth_token_secret
+ ", api_url_format=" + api_url_format + ", same_oauth_signing_url=" + same_oauth_signing_url + "}";
}
}

View File

@ -7,11 +7,13 @@ import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import java.io.IOException;
import java.util.List;
@JsonObject
@ParcelablePlease(allFields = false)
public class ParcelableMediaUpdate implements Parcelable {
public static final Parcelable.Creator<ParcelableMediaUpdate> CREATOR = new Parcelable.Creator<ParcelableMediaUpdate>() {

View File

@ -30,7 +30,18 @@ import android.text.TextUtils;
import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import org.mariotaku.twidere.api.twitter.model.CardEntity;
import org.mariotaku.twidere.api.twitter.model.CardEntity.BindingValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.BooleanValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.ImageValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.StringValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.UserValue;
import org.mariotaku.twidere.api.twitter.model.Place;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.util.HtmlEscapeHelper;
import org.mariotaku.twidere.util.TwitterContentUtils;
@ -43,24 +54,16 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.mariotaku.twidere.api.twitter.model.CardEntity;
import org.mariotaku.twidere.api.twitter.model.CardEntity.BindingValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.BooleanValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.ImageValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.StringValue;
import org.mariotaku.twidere.api.twitter.model.CardEntity.UserValue;
import org.mariotaku.twidere.api.twitter.model.Place;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.User;
@SuppressWarnings("unused")
@JsonObject
@ParcelablePlease(allFields = false)
public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus> {
public static final Parcelable.Creator<ParcelableStatus> CREATOR = new Parcelable.Creator<ParcelableStatus>() {
@Override
public ParcelableStatus createFromParcel(final Parcel in) {
return new ParcelableStatus(in);
ParcelableStatus status = new ParcelableStatus();
ParcelableStatusParcelablePlease.readFromParcel(status, in);
return status;
}
@Override
@ -68,8 +71,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
return new ParcelableStatus[size];
}
};
@JsonField(name = "id")
public long id;
public static final Comparator<ParcelableStatus> REVERSE_ID_COMPARATOR = new Comparator<ParcelableStatus>() {
@Override
@ -80,10 +82,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
return (int) diff;
}
};
@JsonField(name = "account_id")
public long account_id;
@JsonField(name = "timestamp")
public long timestamp;
public static final Comparator<ParcelableStatus> TIMESTAMP_COMPARATOR = new Comparator<ParcelableStatus>() {
@Override
@ -94,111 +93,164 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
return (int) diff;
}
};
@ParcelableThisPlease
@JsonField(name = "id")
public long id;
@ParcelableThisPlease
@JsonField(name = "account_id")
public long account_id;
@ParcelableThisPlease
@JsonField(name = "timestamp")
public long timestamp;
@ParcelableThisPlease
@JsonField(name = "user_id")
public long user_id;
@ParcelableThisPlease
@JsonField(name = "retweet_id")
public long retweet_id;
@ParcelableThisPlease
@JsonField(name = "retweeted_by_user_id")
public long retweeted_by_user_id;
@ParcelableThisPlease
@JsonField(name = "retweet_timestamp")
public long retweet_timestamp;
@ParcelableThisPlease
@JsonField(name = "retweet_count")
public long retweet_count;
@ParcelableThisPlease
@JsonField(name = "favorite_count")
public long favorite_count;
@ParcelableThisPlease
@JsonField(name = "reply_count")
public long reply_count;
@ParcelableThisPlease
@JsonField(name = "descendent_reply_count")
public long descendent_reply_count;
@ParcelableThisPlease
@JsonField(name = "in_reply_to_status_id")
public long in_reply_to_status_id;
@ParcelableThisPlease
@JsonField(name = "in_reply_to_user_id")
public long in_reply_to_user_id;
@ParcelableThisPlease
@JsonField(name = "my_retweet_id")
public long my_retweet_id;
@ParcelableThisPlease
@JsonField(name = "quote_id")
public long quote_id;
@ParcelableThisPlease
@JsonField(name = "quote_timestamp")
public long quote_timestamp;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_id")
public long quoted_by_user_id;
@ParcelableThisPlease
@JsonField(name = "is_gap")
public boolean is_gap;
@ParcelableThisPlease
@JsonField(name = "is_retweet")
public boolean is_retweet;
@ParcelableThisPlease
@JsonField(name = "is_favorite")
public boolean is_favorite;
@ParcelableThisPlease
@JsonField(name = "is_possibly_sensitive")
public boolean is_possibly_sensitive;
@ParcelableThisPlease
@JsonField(name = "user_is_following")
public boolean user_is_following;
@ParcelableThisPlease
@JsonField(name = "user_is_protected")
public boolean user_is_protected;
@ParcelableThisPlease
@JsonField(name = "user_is_verified")
public boolean user_is_verified;
@ParcelableThisPlease
@JsonField(name = "is_quote")
public boolean is_quote;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_is_protected")
public boolean quoted_by_user_is_protected;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_is_verified")
public boolean quoted_by_user_is_verified;
@ParcelableThisPlease
@JsonField(name = "retweeted_by_user_name")
public String retweeted_by_user_name;
@ParcelableThisPlease
@JsonField(name = "retweeted_by_user_screen_name")
public String retweeted_by_user_screen_name;
@ParcelableThisPlease
@JsonField(name = "retweeted_by_user_profile_image")
public String retweeted_by_user_profile_image;
@ParcelableThisPlease
@JsonField(name = "text_html")
public String text_html;
@ParcelableThisPlease
@JsonField(name = "text_plain")
public String text_plain;
@ParcelableThisPlease
@JsonField(name = "user_name")
public String user_name;
@ParcelableThisPlease
@JsonField(name = "user_screen_name")
public String user_screen_name;
@ParcelableThisPlease
@JsonField(name = "in_reply_to_name")
public String in_reply_to_name;
@ParcelableThisPlease
@JsonField(name = "in_reply_to_screen_name")
public String in_reply_to_screen_name;
@ParcelableThisPlease
@JsonField(name = "source")
public String source;
@ParcelableThisPlease
@JsonField(name = "user_profile_image_url")
public String user_profile_image_url;
@ParcelableThisPlease
@JsonField(name = "text_unescaped")
public String text_unescaped;
@ParcelableThisPlease
@JsonField(name = "card_name")
public String card_name;
@ParcelableThisPlease
@JsonField(name = "quote_text_html")
public String quote_text_html;
@ParcelableThisPlease
@JsonField(name = "quote_text_plain")
public String quote_text_plain;
@ParcelableThisPlease
@JsonField(name = "quote_text_unescaped")
public String quote_text_unescaped;
@ParcelableThisPlease
@JsonField(name = "quote_source")
public String quote_source;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_name")
public String quoted_by_user_name;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_screen_name")
public String quoted_by_user_screen_name;
@ParcelableThisPlease
@JsonField(name = "quoted_by_user_profile_image")
public String quoted_by_user_profile_image;
@ParcelableThisPlease
@JsonField(name = "location")
public ParcelableLocation location;
@ParcelableThisPlease
@JsonField(name = "place_full_name")
public String place_full_name;
@ParcelableThisPlease
@JsonField(name = "mentions")
public ParcelableUserMention[] mentions;
@ParcelableThisPlease
@JsonField(name = "media")
public ParcelableMedia[] media;
@ParcelableThisPlease
@JsonField(name = "quote_media")
public ParcelableMedia[] quote_media;
@ParcelableThisPlease
@JsonField(name = "card")
public ParcelableCardEntity card;
@ -264,62 +316,6 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
public ParcelableStatus() {
}
public ParcelableStatus(final Parcel in) {
id = in.readLong();
account_id = in.readLong();
timestamp = in.readLong();
user_id = in.readLong();
retweet_id = in.readLong();
retweet_timestamp = in.readLong();
retweeted_by_user_id = in.readLong();
retweet_count = in.readLong();
favorite_count = in.readLong();
reply_count = in.readLong();
descendent_reply_count = in.readLong();
in_reply_to_status_id = in.readLong();
is_gap = in.readByte() == 1;
is_retweet = in.readByte() == 1;
is_favorite = in.readByte() == 1;
user_is_protected = in.readByte() == 1;
user_is_verified = in.readByte() == 1;
retweeted_by_user_name = in.readString();
retweeted_by_user_screen_name = in.readString();
retweeted_by_user_profile_image = in.readString();
text_html = in.readString();
text_plain = in.readString();
user_name = in.readString();
user_screen_name = in.readString();
in_reply_to_screen_name = in.readString();
source = in.readString();
user_profile_image_url = in.readString();
media = in.createTypedArray(ParcelableMedia.CREATOR);
quote_media = in.createTypedArray(ParcelableMedia.CREATOR);
location = in.readParcelable(ParcelableLocation.class.getClassLoader());
my_retweet_id = in.readLong();
is_possibly_sensitive = in.readByte() == 1;
user_is_following = in.readByte() == 1;
text_unescaped = in.readString();
in_reply_to_user_id = in.readLong();
in_reply_to_name = in.readString();
mentions = in.createTypedArray(ParcelableUserMention.CREATOR);
card = in.readParcelable(ParcelableCardEntity.class.getClassLoader());
place_full_name = in.readString();
is_quote = in.readByte() == 1;
quote_id = in.readLong();
quote_text_html = in.readString();
quote_text_plain = in.readString();
quote_text_unescaped = in.readString();
quote_timestamp = in.readLong();
quoted_by_user_id = in.readLong();
quoted_by_user_name = in.readString();
quoted_by_user_screen_name = in.readString();
quoted_by_user_profile_image = in.readString();
quoted_by_user_is_protected = in.readByte() == 1;
quoted_by_user_is_verified = in.readByte() == 1;
quote_source = in.readString();
card_name = card != null ? card.name : null;
}
public ParcelableStatus(final ParcelableStatus orig, final long override_my_retweet_id,
final long override_retweet_count) {
id = orig.id;
@ -847,58 +843,7 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
@Override
public void writeToParcel(final Parcel out, final int flags) {
out.writeLong(id);
out.writeLong(account_id);
out.writeLong(timestamp);
out.writeLong(user_id);
out.writeLong(retweet_id);
out.writeLong(retweet_timestamp);
out.writeLong(retweeted_by_user_id);
out.writeLong(retweet_count);
out.writeLong(favorite_count);
out.writeLong(reply_count);
out.writeLong(descendent_reply_count);
out.writeLong(in_reply_to_status_id);
out.writeByte((byte) (is_gap ? 1 : 0));
out.writeByte((byte) (is_retweet ? 1 : 0));
out.writeByte((byte) (is_favorite ? 1 : 0));
out.writeByte((byte) (user_is_protected ? 1 : 0));
out.writeByte((byte) (user_is_verified ? 1 : 0));
out.writeString(retweeted_by_user_name);
out.writeString(retweeted_by_user_screen_name);
out.writeString(retweeted_by_user_profile_image);
out.writeString(text_html);
out.writeString(text_plain);
out.writeString(user_name);
out.writeString(user_screen_name);
out.writeString(in_reply_to_screen_name);
out.writeString(source);
out.writeString(user_profile_image_url);
out.writeTypedArray(media, flags);
out.writeTypedArray(quote_media, flags);
out.writeParcelable(location, flags);
out.writeLong(my_retweet_id);
out.writeByte((byte) (is_possibly_sensitive ? 1 : 0));
out.writeByte((byte) (user_is_following ? 1 : 0));
out.writeString(text_unescaped);
out.writeLong(in_reply_to_user_id);
out.writeString(in_reply_to_name);
out.writeTypedArray(mentions, flags);
out.writeParcelable(card, flags);
out.writeString(place_full_name);
out.writeByte((byte) (is_quote ? 1 : 0));
out.writeLong(quote_id);
out.writeString(quote_text_html);
out.writeString(quote_text_plain);
out.writeString(quote_text_unescaped);
out.writeLong(quote_timestamp);
out.writeLong(quoted_by_user_id);
out.writeString(quoted_by_user_name);
out.writeString(quoted_by_user_screen_name);
out.writeString(quoted_by_user_profile_image);
out.writeByte((byte) (quoted_by_user_is_protected ? 1 : 0));
out.writeByte((byte) (quoted_by_user_is_verified ? 1 : 0));
out.writeString(quote_source);
ParcelableStatusParcelablePlease.writeToParcel(this, out, flags);
}
}

View File

@ -59,11 +59,6 @@ public interface TwidereDataStore {
public static interface Accounts extends BaseColumns {
int AUTH_TYPE_OAUTH = 0;
int AUTH_TYPE_XAUTH = 1;
int AUTH_TYPE_BASIC = 2;
int AUTH_TYPE_TWIP_O_MODE = 3;
String TABLE_NAME = "accounts";
String CONTENT_PATH = TABLE_NAME;
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);

View File

@ -40,6 +40,7 @@ import org.mariotaku.twidere.api.twitter.model.Trends;
import org.mariotaku.twidere.api.twitter.model.UrlEntity;
import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.ParcelableMedia;
@ -76,7 +77,7 @@ public final class ContentValuesCreator implements TwidereConstants {
if (basicUsername == null || basicPassword == null) return null;
values.put(Accounts.BASIC_AUTH_USERNAME, basicUsername);
values.put(Accounts.BASIC_AUTH_PASSWORD, basicPassword);
values.put(Accounts.AUTH_TYPE, Accounts.AUTH_TYPE_BASIC);
values.put(Accounts.AUTH_TYPE, ParcelableCredentials.AUTH_TYPE_BASIC);
values.put(Accounts.ACCOUNT_ID, user.getId());
values.put(Accounts.SCREEN_NAME, user.getScreenName());
values.put(Accounts.NAME, user.getName());
@ -118,7 +119,7 @@ public final class ContentValuesCreator implements TwidereConstants {
final boolean noVersionSuffix) {
if (user == null || user.getId() <= 0) return null;
final ContentValues values = new ContentValues();
values.put(Accounts.AUTH_TYPE, Accounts.AUTH_TYPE_TWIP_O_MODE);
values.put(Accounts.AUTH_TYPE, ParcelableCredentials.AUTH_TYPE_TWIP_O_MODE);
values.put(Accounts.ACCOUNT_ID, user.getId());
values.put(Accounts.SCREEN_NAME, user.getScreenName());
values.put(Accounts.NAME, user.getName());

View File

@ -25,11 +25,11 @@ import com.bluelinelabs.logansquare.LoganSquare;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.mariotaku.simplerestapi.http.Endpoint;
import org.mariotaku.simplerestapi.http.RestHttpClient;
import org.mariotaku.simplerestapi.http.RestHttpRequest;
import org.mariotaku.simplerestapi.http.RestHttpResponse;
import org.mariotaku.simplerestapi.method.GET;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.http.Endpoint;
import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.restfu.http.RestHttpRequest;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.HtmlLinkExtractor.HtmlLink;

View File

@ -0,0 +1,279 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.util;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Pair;
import org.mariotaku.restfu.ExceptionFactory;
import org.mariotaku.restfu.HttpRequestFactory;
import org.mariotaku.restfu.RequestInfoFactory;
import org.mariotaku.restfu.RestMethodInfo;
import org.mariotaku.restfu.RestRequestInfo;
import org.mariotaku.restfu.annotation.RestMethod;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
import org.mariotaku.restfu.http.FileValue;
import org.mariotaku.restfu.http.RestHttpRequest;
import org.mariotaku.restfu.http.RestHttpResponse;
import org.mariotaku.restfu.http.mime.StringTypedData;
import org.mariotaku.restfu.http.mime.TypedData;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.TwitterOAuth;
import org.mariotaku.twidere.api.twitter.TwitterUpload;
import org.mariotaku.twidere.api.twitter.TwitterUserStream;
import org.mariotaku.twidere.api.twitter.auth.BasicAuthorization;
import org.mariotaku.twidere.api.twitter.auth.EmptyAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
import org.mariotaku.twidere.api.twitter.util.TwitterConverter;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.ParcelableCredentials;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static android.text.TextUtils.isEmpty;
/**
* Created by mariotaku on 15/5/26.
*/
public class TwitterAPIUtils implements TwidereConstants {
public static Endpoint getEndpoint(ParcelableCredentials credentials, Class<?> cls) {
final String apiUrlFormat;
final boolean sameOAuthSigningUrl = credentials.same_oauth_signing_url;
final boolean noVersionSuffix = credentials.no_version_suffix;
if (!isEmpty(credentials.api_url_format)) {
apiUrlFormat = credentials.api_url_format;
} else {
apiUrlFormat = DEFAULT_TWITTER_API_URL_FORMAT;
}
final String domain, versionSuffix;
if (Twitter.class.isAssignableFrom(cls)) {
domain = "api";
versionSuffix = noVersionSuffix ? null : "/1.1/";
} else if (TwitterUpload.class.isAssignableFrom(cls)) {
domain = "upload";
versionSuffix = noVersionSuffix ? null : "/1.1/";
} else if (TwitterOAuth.class.isAssignableFrom(cls)) {
domain = "api";
versionSuffix = "oauth";
} else if (TwitterUserStream.class.isAssignableFrom(cls)) {
domain = "userstream";
versionSuffix = noVersionSuffix ? null : "/1.1/";
} else {
throw new TwitterConverter.UnsupportedTypeException(cls);
}
final String endpointUrl;
endpointUrl = getApiUrl(apiUrlFormat, domain, versionSuffix);
if (credentials.auth_type == ParcelableCredentials.AUTH_TYPE_XAUTH || credentials.auth_type == ParcelableCredentials.AUTH_TYPE_OAUTH) {
final String signEndpointUrl;
if (!sameOAuthSigningUrl) {
signEndpointUrl = getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, domain, versionSuffix);
} else {
signEndpointUrl = endpointUrl;
}
return new OAuthEndpoint(endpointUrl, signEndpointUrl);
}
return new Endpoint(endpointUrl);
}
public static Authorization getAuthorization(ParcelableCredentials credentials) {
switch (credentials.auth_type) {
case ParcelableCredentials.AUTH_TYPE_OAUTH:
case ParcelableCredentials.AUTH_TYPE_XAUTH: {
final String consumerKey = TextUtils.isEmpty(credentials.consumer_key) ?
TWITTER_CONSUMER_KEY_LEGACY : credentials.consumer_key;
final String consumerSecret = TextUtils.isEmpty(credentials.consumer_secret) ?
TWITTER_CONSUMER_SECRET_LEGACY : credentials.consumer_secret;
final OAuthToken accessToken = new OAuthToken(credentials.oauth_token, credentials.oauth_token_secret);
return new OAuthAuthorization(consumerKey, consumerSecret, accessToken);
}
case ParcelableCredentials.AUTH_TYPE_BASIC: {
final String screenName = credentials.screen_name;
final String username = credentials.basic_auth_username;
final String loginName = username != null ? username : screenName;
final String password = credentials.basic_auth_password;
if (isEmpty(loginName) || isEmpty(password)) return null;
return new BasicAuthorization(loginName, password);
}
}
return new EmptyAuthorization();
}
private static void addParameter(List<Pair<String, String>> params, String name, Object value) {
params.add(Pair.create(name, String.valueOf(value)));
}
private static void addPart(List<Pair<String, TypedData>> params, String name, Object value) {
final TypedData typedData = new StringTypedData(String.valueOf(value), Charset.defaultCharset());
params.add(Pair.create(name, typedData));
}
public static String getApiBaseUrl(String format, final String domain) {
if (format == null) return null;
final Matcher matcher = Pattern.compile("\\[(\\.?)DOMAIN(\\.?)\\]").matcher(format);
if (!matcher.find()) {
// For backward compatibility
format = substituteLegacyApiBaseUrl(format, domain);
if (!format.endsWith("/1.1") && !format.endsWith("/1.1/")) {
return format;
}
final String versionSuffix = "/1.1";
final int suffixLength = versionSuffix.length();
final int lastIndex = format.lastIndexOf(versionSuffix);
return format.substring(0, lastIndex) + format.substring(lastIndex + suffixLength);
}
if (TextUtils.isEmpty(domain)) return matcher.replaceAll("");
return matcher.replaceAll(String.format("$1%s$2", domain));
}
private static String substituteLegacyApiBaseUrl(@NonNull String format, String domain) {
final int startOfHost = format.indexOf("://") + 3, endOfHost = format.indexOf('/', startOfHost);
final String host = endOfHost != -1 ? format.substring(startOfHost, endOfHost) : format.substring(startOfHost);
if (!host.equalsIgnoreCase("api.twitter.com")) return format;
return format.substring(0, startOfHost) + domain + ".twitter.com" + format.substring(endOfHost);
}
public static String getApiUrl(final String pattern, final String domain, final String appendPath) {
final String urlBase = getApiBaseUrl(pattern, domain);
if (urlBase == null) return null;
if (appendPath == null) return urlBase.endsWith("/") ? urlBase : urlBase + "/";
final StringBuilder sb = new StringBuilder(urlBase);
if (urlBase.endsWith("/")) {
sb.append(appendPath.startsWith("/") ? appendPath.substring(1) : appendPath);
} else {
if (appendPath.startsWith("/")) {
sb.append(appendPath);
} else {
sb.append('/');
sb.append(appendPath);
}
}
return sb.toString();
}
public static String getUserAgentName(ConsumerKeyType type) {
switch (type) {
case TWITTER_FOR_ANDROID: {
return "TwitterAndroid";
}
case TWITTER_FOR_IPHONE: {
return "Twitter-iPhone";
}
case TWITTER_FOR_IPAD: {
return "Twitter-iPad";
}
case TWITTER_FOR_MAC: {
return "Twitter-Mac";
}
}
return "Twitter";
}
public static String getTwidereUserAgent(final Context context) {
final PackageManager pm = context.getPackageManager();
try {
final PackageInfo pi = pm.getPackageInfo(TWIDERE_PACKAGE_NAME, 0);
return TWIDERE_APP_NAME + " " + TWIDERE_PROJECT_URL + " / " + pi.versionName;
} catch (final PackageManager.NameNotFoundException e) {
return TWIDERE_APP_NAME + " " + TWIDERE_PROJECT_URL;
}
}
public static class TwidereRequestInfoFactory implements RequestInfoFactory {
@Override
public RestRequestInfo create(RestMethodInfo methodInfo) {
final RestMethod method = methodInfo.getMethod();
final String path = methodInfo.getPath();
final List<Pair<String, String>> queries = new ArrayList<>(methodInfo.getQueries());
final List<Pair<String, String>> forms = new ArrayList<>(methodInfo.getForms());
final List<Pair<String, String>> headers = methodInfo.getHeaders();
final List<Pair<String, TypedData>> parts = methodInfo.getParts();
final FileValue file = methodInfo.getFile();
final Map<String, Object> extras = methodInfo.getExtras();
if (parts.isEmpty()) {
final List<Pair<String, String>> params = method.hasBody() ? forms : queries;
addParameter(params, "include_cards", true);
addParameter(params, "cards_platform", "Android-12");
addParameter(params, "include_entities", true);
addParameter(params, "include_my_retweet", 1);
addParameter(params, "include_rts", 1);
addParameter(params, "include_reply_count", true);
addParameter(params, "include_descendent_reply_count", true);
} else {
addPart(parts, "include_cards", true);
addPart(parts, "cards_platform", "Android-12");
addPart(parts, "include_entities", true);
addPart(parts, "include_my_retweet", 1);
addPart(parts, "include_rts", 1);
addPart(parts, "include_reply_count", true);
addPart(parts, "include_descendent_reply_count", true);
}
return new RestRequestInfo(method.value(), path, queries, forms, headers, parts, file,
methodInfo.getBody(), extras);
}
}
public static class TwidereHttpRequestFactory implements HttpRequestFactory {
private final String userAgent;
public TwidereHttpRequestFactory(final String userAgent) {
this.userAgent = userAgent;
}
@Override
public RestHttpRequest create(@NonNull Endpoint endpoint, @NonNull RestRequestInfo info,
@Nullable Authorization authorization) {
final String restMethod = info.getMethod();
final String url = Endpoint.constructUrl(endpoint.getUrl(), info);
final ArrayList<Pair<String, String>> headers = new ArrayList<>(info.getHeaders());
if (authorization != null && authorization.hasAuthorization()) {
headers.add(Pair.create("Authorization", authorization.getHeader(endpoint, info)));
}
headers.add(Pair.create("User-Agent", userAgent));
return new RestHttpRequest(restMethod, url, headers, info.getBody(), null);
}
}
public static class TwidereExceptionFactory implements ExceptionFactory {
@Override
public Exception newException(Throwable cause, RestHttpRequest request, RestHttpResponse response) {
final TwitterException te = new TwitterException(cause);
te.setResponse(response);
return te;
}
}
}

View File

@ -29,6 +29,16 @@ android {
versionCode 14
versionName "1.12 (0.3.0-dev)"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
}
buildTypes {
release {
minifyEnabled false
@ -42,8 +52,7 @@ android {
}
dependencies {
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile project(':twidere.library.extension')
compile project(':twidere.component.twitter4j.streaming')
compile fileTree(dir: 'libs', include: ['*.jar'])
}

Some files were not shown because too many files have changed in this diff Show More