redesigning dashboard
|
@ -23,4 +23,15 @@ android {
|
|||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
|
@ -7,6 +7,5 @@ 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'
|
|
@ -45,7 +45,6 @@ dependencies {
|
|||
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'])
|
||||
}
|
||||
|
|
|
@ -189,6 +189,8 @@ public interface SharedPreferenceConstants {
|
|||
String KEY_MENTIONS_NOTIFICATION = "mentions_notification";
|
||||
@Preference(type = BOOLEAN)
|
||||
String KEY_DIRECT_MESSAGES_NOTIFICATION = "direct_messages_notification";
|
||||
@Preference(type = BOOLEAN)
|
||||
String KEY_ENABLE_STREAMING = "enable_streaming";
|
||||
@Preference(type = INT)
|
||||
String KEY_LOCAL_TRENDS_WOEID = "local_trends_woeid";
|
||||
String KEY_NOTIFICATION_RINGTONE = "notification_ringtone";
|
||||
|
|
|
@ -26,14 +26,17 @@ import android.text.TextUtils;
|
|||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
import org.mariotaku.twidere.api.twitter.model.Status;
|
||||
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@JsonObject
|
||||
@ParcelablePlease(allFields = false)
|
||||
public class ParcelableUserMention implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<ParcelableUserMention> CREATOR = new Parcelable.Creator<ParcelableUserMention>() {
|
||||
|
@ -48,10 +51,13 @@ public class ParcelableUserMention implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "id")
|
||||
public long id;
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "name")
|
||||
public String name;
|
||||
@ParcelableThisPlease
|
||||
@JsonField(name = "screen_name")
|
||||
public String screen_name;
|
||||
|
||||
|
@ -87,8 +93,15 @@ public class ParcelableUserMention implements Parcelable {
|
|||
}
|
||||
|
||||
public static ParcelableUserMention[] fromSerializedJson(String string) {
|
||||
|
||||
return new ParcelableUserMention[0];
|
||||
if (string == null) return null;
|
||||
final List<ParcelableUserMention> list;
|
||||
try {
|
||||
list = LoganSquare.parseList(string, ParcelableUserMention.class);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
if (list == null) return null;
|
||||
return list.toArray(new ParcelableUserMention[list.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,279 +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.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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,58 +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/>.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: rootProject.file('global.gradle')
|
||||
apply from: rootProject.file('signing.gradle')
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
applicationId "org.mariotaku.twidere.extension.streaming"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
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
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.squareup.okhttp:okhttp:2.4.0'
|
||||
compile project(':twidere.library.extension')
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/mariotaku/Tools/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.mariotaku.twidere.extension.streaming">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
<meta-data
|
||||
android:name="org.mariotaku.twidere.extension"
|
||||
android:value="true"/>
|
||||
<meta-data
|
||||
android:name="org.mariotaku.twidere.extension.permissions"
|
||||
android:value="read|write|direct_messages|accounts|refresh|preferences"/>
|
||||
<meta-data
|
||||
android:name="org.mariotaku.twidere.extension.settings"
|
||||
android:value="org.mariotaku.twidere.extension.streaming.SettingsActivity"/>
|
||||
|
||||
<receiver android:name="TwidereLaunchReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.HOME_ACTIVITY_ONCREATE"/>
|
||||
<action android:name="org.mariotaku.twidere.HOME_ACTIVITY_ONDESTROY"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name="StreamingService"/>
|
||||
|
||||
<activity
|
||||
android:name="SettingsActivity"
|
||||
android:exported="true">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -1,12 +0,0 @@
|
|||
package org.mariotaku.twidere.extension.streaming;
|
||||
|
||||
import org.mariotaku.twidere.TwidereConstants;
|
||||
|
||||
public interface Constants extends TwidereConstants {
|
||||
|
||||
public static final String LOGTAG = "Twidere.Streaming";
|
||||
|
||||
public static final String PREFERENCE_KEY_ACCOUNT_IDS = "account_ids";
|
||||
public static final String PREFERENCE_KEY_ENABLE_STREAMING = "enable_streaming";
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package org.mariotaku.twidere.extension.streaming;
|
||||
|
||||
public interface PrivateConstants {
|
||||
|
||||
public static final String TWITTER_CONSUMER_KEY = "uAFVpMhBntJutfVj6abfA";
|
||||
public static final String TWITTER_CONSUMER_SECRET = "JARXkJTfxo0F8MyctYy9bUmrLISjo8vXAHsZHYuk2E";
|
||||
|
||||
public static final String MAPS_API_KEY_RELEASE = "0kjPwJOe_zwYjzGc9uYak7vhm_Sf3eob-2L3Xzw";
|
||||
public static final String MAPS_API_KEY_DEBUG = "0kjPwJOe_zwY9p6kT-kygu4mxwysyOOpfkaXqTA";
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package org.mariotaku.twidere.extension.streaming;
|
||||
|
||||
import org.mariotaku.twidere.Twidere;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class SettingsActivity extends PreferenceActivity implements Constants, OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final int REQUEST_REQUEST_PERMISSIONS = 101;
|
||||
private SharedPreferences mPreferences;
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(final SharedPreferences preferences, final String key) {
|
||||
if (PREFERENCE_KEY_ENABLE_STREAMING.equals(key)) {
|
||||
final Intent intent = new Intent(this, StreamingService.class);
|
||||
if (preferences.getBoolean(key, true)) {
|
||||
startService(intent);
|
||||
} else {
|
||||
stopService(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_REQUEST_PERMISSIONS: {
|
||||
if (resultCode != RESULT_OK) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final boolean granted;
|
||||
try {
|
||||
granted = Twidere.isPermissionGranted(this);
|
||||
} catch (final SecurityException e) {
|
||||
// TODO show error
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (!granted) {
|
||||
final Intent intent = new Intent(Twidere.INTENT_ACTION_REQUEST_PERMISSIONS);
|
||||
intent.setPackage("org.mariotaku.twidere");
|
||||
try {
|
||||
startActivityForResult(intent, REQUEST_REQUEST_PERMISSIONS);
|
||||
} catch (final ActivityNotFoundException e) {
|
||||
|
||||
}
|
||||
}
|
||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
mPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package org.mariotaku.twidere.extension.streaming;
|
||||
|
||||
import org.mariotaku.twidere.Twidere;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class TwidereLaunchReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
final Intent service_intent = new Intent(context, StreamingService.class);
|
||||
if (Twidere.BROADCAST_HOME_ACTIVITY_ONCREATE.equals(action)) {
|
||||
context.startService(service_intent);
|
||||
} else if (Twidere.BROADCAST_HOME_ACTIVITY_ONDESTROY.equals(action)) {
|
||||
context.stopService(service_intent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012 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.extension.streaming.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
|
||||
public final class ActivityAccessor {
|
||||
|
||||
public static void onBackPressed(final Activity activity) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) return;
|
||||
OnBackPressedAccessor.onBackPressed(activity);
|
||||
}
|
||||
|
||||
public static void overridePendingTransition(final Activity activity, final int enter_anim, final int exit_anim) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) return;
|
||||
OverridePendingTransitionAccessor.overridePendingTransition(activity, enter_anim, exit_anim);
|
||||
}
|
||||
|
||||
public static void setHomeButtonEnabled(final Activity activity, final boolean enabled) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) return;
|
||||
SetHomeButtonEnabledAccessor.setHomeButtonEnabled(activity, enabled);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
||||
private static class OnBackPressedAccessor {
|
||||
|
||||
private static void onBackPressed(final Activity activity) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) return;
|
||||
activity.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ECLAIR)
|
||||
private static class OverridePendingTransitionAccessor {
|
||||
private static void overridePendingTransition(final Activity activity, final int enter_anim, final int exit_anim) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) return;
|
||||
activity.overridePendingTransition(enter_anim, exit_anim);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
private static class SetHomeButtonEnabledAccessor {
|
||||
|
||||
private static void setHomeButtonEnabled(final Activity activity, final boolean enabled) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) return;
|
||||
final ActionBar action_bar = activity.getActionBar();
|
||||
action_bar.setHomeButtonEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,197 +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.twidere.extension.streaming.util;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.squareup.okhttp.Call;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
|
||||
import org.mariotaku.restfu.Utils;
|
||||
import org.mariotaku.restfu.http.ContentType;
|
||||
import org.mariotaku.restfu.http.RestHttpClient;
|
||||
import org.mariotaku.restfu.http.RestHttpRequest;
|
||||
import org.mariotaku.restfu.http.RestHttpResponse;
|
||||
import org.mariotaku.restfu.http.mime.TypedData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import okio.BufferedSink;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/5/5.
|
||||
*/
|
||||
public class OkHttpRestClient implements RestHttpClient {
|
||||
|
||||
private final OkHttpClient client;
|
||||
|
||||
public OkHttpRestClient() {
|
||||
this(new OkHttpClient());
|
||||
}
|
||||
|
||||
public OkHttpRestClient(OkHttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RestHttpResponse execute(RestHttpRequest restHttpRequest) throws IOException {
|
||||
final Request.Builder builder = new Request.Builder();
|
||||
builder.method(restHttpRequest.getMethod(), RestToOkBody.wrap(restHttpRequest.getBody()));
|
||||
builder.url(restHttpRequest.getUrl());
|
||||
final List<Pair<String, String>> headers = restHttpRequest.getHeaders();
|
||||
if (headers != null) {
|
||||
for (Pair<String, String> header : headers) {
|
||||
builder.addHeader(header.first, header.second);
|
||||
}
|
||||
}
|
||||
final Call call = client.newCall(builder.build());
|
||||
return new OkRestHttpResponse(call.execute());
|
||||
}
|
||||
|
||||
private static class RestToOkBody extends RequestBody {
|
||||
private final TypedData body;
|
||||
|
||||
public RestToOkBody(TypedData body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
final ContentType contentType = body.contentType();
|
||||
if (contentType == null) return null;
|
||||
return MediaType.parse(contentType.toHeader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
body.writeTo(sink.outputStream());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static RequestBody wrap(@Nullable TypedData body) {
|
||||
if (body == null) return null;
|
||||
return new RestToOkBody(body);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OkRestHttpResponse extends RestHttpResponse {
|
||||
private final Response response;
|
||||
private TypedData body;
|
||||
|
||||
public OkRestHttpResponse(Response response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
return response.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<String, String>> getHeaders() {
|
||||
final Headers headers = response.headers();
|
||||
final ArrayList<Pair<String, String>> headersList = new ArrayList<>();
|
||||
for (int i = 0, j = headers.size(); i < j; i++) {
|
||||
headersList.add(Pair.create(headers.name(i), headers.value(i)));
|
||||
}
|
||||
return headersList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
return response.header(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getHeaders(String name) {
|
||||
final List<String> values = response.headers(name);
|
||||
return values.toArray(new String[values.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedData getBody() {
|
||||
if (body != null) return body;
|
||||
return body = new OkToRestBody(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (body != null) {
|
||||
body.close();
|
||||
body = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class OkToRestBody implements TypedData {
|
||||
|
||||
private final ResponseBody body;
|
||||
|
||||
public OkToRestBody(ResponseBody body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentType contentType() {
|
||||
final MediaType mediaType = body.contentType();
|
||||
if (mediaType == null) return null;
|
||||
return ContentType.parse(mediaType.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String contentEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() throws IOException {
|
||||
return body.contentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NonNull OutputStream os) throws IOException {
|
||||
Utils.copyStream(stream(), os);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InputStream stream() throws IOException {
|
||||
return body.byteStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
body.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012 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.extension.streaming.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.internal.Network;
|
||||
|
||||
import org.mariotaku.twidere.Twidere;
|
||||
import org.mariotaku.twidere.extension.streaming.BuildConfig;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class TwidereHostAddressResolver implements Network {
|
||||
|
||||
private static final String RESOLVER_LOGTAG = "Twidere.Streaming.Host";
|
||||
private static TwidereHostAddressResolver sInstance;
|
||||
|
||||
private final HostCache mHostCache = new HostCache(512);
|
||||
private final Context mContext;
|
||||
|
||||
public TwidereHostAddressResolver(final Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress[] resolveInetAddresses(final String host) throws UnknownHostException {
|
||||
if (host == null) return null;
|
||||
// First, I'll try to load address cached.
|
||||
final InetAddress[] cached = mHostCache.get(host);
|
||||
if (cached != null) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(RESOLVER_LOGTAG, "Got cached " + Arrays.toString(cached));
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
final InetAddress[] resolved = Twidere.resolveHost(mContext, host);
|
||||
mHostCache.put(host, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public static Network getInstance(final Context context) {
|
||||
if (sInstance != null) return sInstance;
|
||||
return sInstance = new TwidereHostAddressResolver(context);
|
||||
}
|
||||
|
||||
private static class HostCache extends LinkedHashMap<String, InetAddress[]> {
|
||||
|
||||
private static final long serialVersionUID = -9216545511009449147L;
|
||||
|
||||
HostCache(final int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress[] put(final String key, final InetAddress[] value) {
|
||||
if (value == null) return null;
|
||||
return super.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package org.mariotaku.twidere.extension.streaming.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
|
||||
import org.mariotaku.restfu.RestAPIFactory;
|
||||
import org.mariotaku.restfu.http.Authorization;
|
||||
import org.mariotaku.restfu.http.Endpoint;
|
||||
import org.mariotaku.restfu.http.RestHttpClient;
|
||||
import org.mariotaku.twidere.Twidere;
|
||||
import org.mariotaku.twidere.TwidereConstants;
|
||||
import org.mariotaku.twidere.TwidereSharedPreferences;
|
||||
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
|
||||
import org.mariotaku.twidere.api.twitter.util.TwitterConverter;
|
||||
import org.mariotaku.twidere.model.ConsumerKeyType;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
||||
import org.mariotaku.twidere.util.ParseUtils;
|
||||
import org.mariotaku.twidere.util.TwitterAPIUtils;
|
||||
import org.mariotaku.twidere.util.TwitterContentUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
public class Utils implements TwidereConstants {
|
||||
|
||||
|
||||
public static void closeSilently(Closeable closeable) {
|
||||
if (closeable == null) return;
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static RestHttpClient getDefaultHttpClient(final Context context) {
|
||||
if (context == null) return null;
|
||||
final SharedPreferences prefs = Twidere.getSharedPreferences(context);
|
||||
return createHttpClient(context, prefs);
|
||||
}
|
||||
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint, final Authorization auth, Class<T> cls) {
|
||||
final RestAPIFactory factory = new RestAPIFactory();
|
||||
final String userAgent;
|
||||
if (auth instanceof OAuthAuthorization) {
|
||||
final String consumerKey = ((OAuthAuthorization) auth).getConsumerKey();
|
||||
final String consumerSecret = ((OAuthAuthorization) auth).getConsumerSecret();
|
||||
final ConsumerKeyType officialKeyType = TwitterContentUtils.getOfficialKeyType(context, consumerKey, consumerSecret);
|
||||
if (officialKeyType != ConsumerKeyType.UNKNOWN) {
|
||||
userAgent = TwitterAPIUtils.getUserAgentName(officialKeyType);
|
||||
} else {
|
||||
userAgent = TwitterAPIUtils.getTwidereUserAgent(context);
|
||||
}
|
||||
} else {
|
||||
userAgent = TwitterAPIUtils.getTwidereUserAgent(context);
|
||||
}
|
||||
factory.setClient(getDefaultHttpClient(context));
|
||||
factory.setConverter(new TwitterConverter());
|
||||
factory.setEndpoint(endpoint);
|
||||
factory.setAuthorization(auth);
|
||||
factory.setRequestInfoFactory(new TwitterAPIUtils.TwidereRequestInfoFactory());
|
||||
factory.setHttpRequestFactory(new TwitterAPIUtils.TwidereHttpRequestFactory(userAgent));
|
||||
factory.setExceptionFactory(new TwitterAPIUtils.TwidereExceptionFactory());
|
||||
return factory.build(cls);
|
||||
}
|
||||
|
||||
public static RestHttpClient createHttpClient(final Context context, final SharedPreferences prefs) {
|
||||
final int connectionTimeout = prefs.getInt(KEY_CONNECTION_TIMEOUT, 10);
|
||||
final boolean ignoreSslError = prefs.getBoolean(KEY_IGNORE_SSL_ERROR, false);
|
||||
final boolean enableProxy = prefs.getBoolean(KEY_ENABLE_PROXY, false);
|
||||
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
client.setConnectTimeout(connectionTimeout, TimeUnit.SECONDS);
|
||||
if (ignoreSslError) {
|
||||
client.setSslSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
|
||||
} else {
|
||||
client.setSslSocketFactory(SSLCertificateSocketFactory.getDefault(0, null));
|
||||
}
|
||||
if (enableProxy) {
|
||||
client.setProxy(getProxy(prefs));
|
||||
}
|
||||
Internal.instance.setNetwork(client, TwidereHostAddressResolver.getInstance(context));
|
||||
return new OkHttpRestClient(client);
|
||||
}
|
||||
|
||||
|
||||
public static Proxy getProxy(final SharedPreferences prefs) {
|
||||
final String proxyHost = prefs.getString(KEY_PROXY_HOST, null);
|
||||
final int proxyPort = ParseUtils.parseInt(prefs.getString(KEY_PROXY_PORT, "-1"));
|
||||
if (!isEmpty(proxyHost) && proxyPort >= 0 && proxyPort < 65535) {
|
||||
final SocketAddress addr = InetSocketAddress.createUnresolved(proxyHost, proxyPort);
|
||||
return new Proxy(Proxy.Type.HTTP, addr);
|
||||
}
|
||||
return Proxy.NO_PROXY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static long[] getActivatedAccountIds(final Context context) {
|
||||
long[] accounts = new long[0];
|
||||
if (context == null) return accounts;
|
||||
final String[] cols = new String[]{Accounts.ACCOUNT_ID};
|
||||
final Cursor cur = context.getContentResolver().query(Accounts.CONTENT_URI, cols, Accounts.IS_ACTIVATED + "=1",
|
||||
null, Accounts.ACCOUNT_ID);
|
||||
if (cur != null) {
|
||||
final int idx = cur.getColumnIndexOrThrow(Accounts.ACCOUNT_ID);
|
||||
cur.moveToFirst();
|
||||
accounts = new long[cur.getCount()];
|
||||
int i = 0;
|
||||
while (!cur.isAfterLast()) {
|
||||
accounts[i] = cur.getLong(idx);
|
||||
i++;
|
||||
cur.moveToNext();
|
||||
}
|
||||
cur.close();
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
public static String getNonEmptyString(final TwidereSharedPreferences pref, final String key, final String def) {
|
||||
if (pref == null) return def;
|
||||
final String val = pref.getString(key, def);
|
||||
return isEmpty(val) ? def : val;
|
||||
}
|
||||
|
||||
public static String replaceLast(final String text, final String regex, final String replacement) {
|
||||
if (text == null || regex == null || replacement == null) return text;
|
||||
return text.replaceFirst("(?s)" + regex + "(?!.*?" + regex + ")", replacement);
|
||||
}
|
||||
|
||||
}
|
Before Width: | Height: | Size: 764 B |
Before Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 783 B |
Before Width: | Height: | Size: 791 B |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 402 B |
Before Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 529 B |
Before Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 869 B |
Before Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 918 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1010 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 10 KiB |
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<bool name="hires_profile_image">true</bool>
|
||||
|
||||
</resources>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<bool name="hires_profile_image">false</bool>
|
||||
|
||||
</resources>
|
|
@ -1,9 +0,0 @@
|
|||
<resources>
|
||||
|
||||
<string name="app_name">Twidere Streaming Extension</string>
|
||||
<string name="select_accounts">Select accounts</string>
|
||||
<string name="enable_streaming">Enable streaming</string>
|
||||
<string name="streaming_service_running">Streaming service is running</string>
|
||||
<string name="request_permission">Permission request to access Twidere</string>
|
||||
|
||||
</resources>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="enable_streaming"
|
||||
android:title="@string/enable_streaming"/>
|
||||
|
||||
</PreferenceScreen>
|
|
@ -22,16 +22,6 @@ android {
|
|||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
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'
|
||||
}
|
||||
productFlavors {
|
||||
google {
|
||||
}
|
||||
|
@ -95,11 +85,14 @@ dependencies {
|
|||
compile 'com.bluelinelabs:logansquare:1.1.0'
|
||||
compile 'ch.acra:acra:4.6.2'
|
||||
compile 'org.jraf:android-switch-backport:2.0.1'
|
||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4'
|
||||
googleCompile 'com.google.android.gms:play-services-maps:7.3.0'
|
||||
googleCompile 'com.google.maps.android:android-maps-utils:0.3.4'
|
||||
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
||||
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
||||
debugCompile 'im.dino:dbinspector:3.1.0@aar'
|
||||
debugCompile 'com.facebook.stetho:stetho:1.1.1'
|
||||
debugCompile 'com.facebook.stetho:stetho-okhttp:1.1.1'
|
||||
compile project(':twidere.component.common')
|
||||
compile project(':twidere.component.nyan')
|
||||
compile fileTree(dir: 'libs/main', include: ['*.jar'])
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.app.Application;
|
||||
|
||||
import com.facebook.stetho.Stetho;
|
||||
import com.facebook.stetho.okhttp.StethoInterceptor;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/5/27.
|
||||
*/
|
||||
public class DebugModeUtils {
|
||||
|
||||
public static void initForHttpClient(final OkHttpClient client) {
|
||||
client.networkInterceptors().add(new StethoInterceptor());
|
||||
}
|
||||
|
||||
public static void initForApplication(final Application application) {
|
||||
Stetho.initialize(Stetho.newInitializerBuilder(application)
|
||||
.enableDumpapp(Stetho.defaultDumperPluginsProvider(application))
|
||||
.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(application))
|
||||
.build());
|
||||
}
|
||||
}
|
|
@ -1,65 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="org.mariotaku.twidere"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.mariotaku.twidere"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-sdk />
|
||||
<uses-sdk/>
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.location"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.location.gps"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.location.network"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.nfc"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true" />
|
||||
android:required="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.SHORTEN_STATUS" />
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.UPLOAD_MEDIA" />
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.SYNC_TIMELINE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.NFC"/>
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.SHORTEN_STATUS"/>
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.UPLOAD_MEDIA"/>
|
||||
<uses-permission android:name="org.mariotaku.twidere.permission.SYNC_TIMELINE"/>
|
||||
|
||||
<permission-group
|
||||
android:name="org.mariotaku.twidere.permission.PERMISSION_GROUP"
|
||||
android:label="@string/app_name" />
|
||||
android:label="@string/app_name"/>
|
||||
|
||||
<permission
|
||||
android:name="org.mariotaku.twidere.permission.SHORTEN_STATUS"
|
||||
android:description="@string/permission_description_shorten_status"
|
||||
android:label="@string/permission_label_shorten_status"
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP" />
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP"/>
|
||||
<permission
|
||||
android:name="org.mariotaku.twidere.permission.UPLOAD_MEDIA"
|
||||
android:description="@string/permission_description_upload_media"
|
||||
android:label="@string/permission_label_upload_media"
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP" />
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP"/>
|
||||
<permission
|
||||
android:name="org.mariotaku.twidere.permission.SYNC_TIMELINE"
|
||||
android:description="@string/permission_description_sync_timeline"
|
||||
android:label="@string/permission_label_sync_timeline"
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP" />
|
||||
android:permissionGroup="org.mariotaku.twidere.permission.PERMISSION_GROUP"/>
|
||||
|
||||
<application
|
||||
android:name=".app.TwidereApplication"
|
||||
|
@ -73,29 +73,29 @@
|
|||
tools:ignore="UnusedAttribute">
|
||||
<uses-library
|
||||
android:name="com.sec.android.app.multiwindow"
|
||||
android:required="false" />
|
||||
android:required="false"/>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
android:value="AEdPqrEAAAAIKbKATV1AGbLB4kem3w8QaPVJSPVVumbMHxkfwA" />
|
||||
android:value="AEdPqrEAAAAIKbKATV1AGbLB4kem3w8QaPVJSPVVumbMHxkfwA"/>
|
||||
<meta-data
|
||||
android:name="com.sec.android.support.multiwindow"
|
||||
android:value="true" />
|
||||
android:value="true"/>
|
||||
<meta-data
|
||||
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W"
|
||||
android:value="480dp" />
|
||||
android:value="480dp"/>
|
||||
<meta-data
|
||||
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H"
|
||||
android:value="640dp" />
|
||||
android:value="640dp"/>
|
||||
<meta-data
|
||||
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W"
|
||||
android:value="240dp" />
|
||||
android:value="240dp"/>
|
||||
<meta-data
|
||||
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H"
|
||||
android:value="320dp" />
|
||||
android:value="320dp"/>
|
||||
<meta-data
|
||||
android:name="override_tinted_status_bar_defaults"
|
||||
android:value="true" />
|
||||
android:value="true"/>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
|
@ -105,11 +105,11 @@
|
|||
android:theme="@style/Theme.Launcher"
|
||||
android:windowSoftInputMode="adjustNothing">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -121,11 +121,11 @@
|
|||
android:theme="@style/Theme.Launcher"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -135,22 +135,22 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.HOME" />
|
||||
<action android:name="org.mariotaku.twidere.HOME"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
android:resource="@xml/searchable"/>
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".activity.support.HomeActivity" />
|
||||
android:value=".activity.support.HomeActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.support.ComposeActivity"
|
||||
|
@ -160,29 +160,29 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.Dialog"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter android:label="@string/compose">
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="org.mariotaku.twidere.COMPOSE" />
|
||||
<action android:name="org.mariotaku.twidere.REPLY" />
|
||||
<action android:name="org.mariotaku.twidere.QUOTE" />
|
||||
<action android:name="org.mariotaku.twidere.EDIT_DRAFT" />
|
||||
<action android:name="org.mariotaku.twidere.MENTION" />
|
||||
<action android:name="org.mariotaku.twidere.REPLY_MULTIPLE" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="org.mariotaku.twidere.COMPOSE"/>
|
||||
<action android:name="org.mariotaku.twidere.REPLY"/>
|
||||
<action android:name="org.mariotaku.twidere.QUOTE"/>
|
||||
<action android:name="org.mariotaku.twidere.EDIT_DRAFT"/>
|
||||
<action android:name="org.mariotaku.twidere.MENTION"/>
|
||||
<action android:name="org.mariotaku.twidere.REPLY_MULTIPLE"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="image/*"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.support.HomeActivity" />
|
||||
android:value=".activity.support.HomeActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.support.QuickSearchBarActivity"
|
||||
|
@ -191,9 +191,9 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.Dialog"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.GLOBAL_SEARCH" />
|
||||
<action android:name="org.mariotaku.twidere.GLOBAL_SEARCH"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -202,9 +202,9 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.DialogWhenLarge.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.TWITTER_LOGIN" />
|
||||
<action android:name="org.mariotaku.twidere.TWITTER_LOGIN"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -213,9 +213,9 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.Dialog"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.TWITTER_BROWSER_LOGIN" />
|
||||
<action android:name="org.mariotaku.twidere.TWITTER_BROWSER_LOGIN"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -224,29 +224,29 @@
|
|||
android:theme="@style/Theme.Twidere.Dark"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
|
||||
<action android:name="org.mariotaku.twidere.SETTINGS" />
|
||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
|
||||
<action android:name="org.mariotaku.twidere.SETTINGS"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.support.HomeActivity" />
|
||||
android:value=".activity.support.HomeActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.support.APIEditorActivity"
|
||||
android:label="@string/edit_api"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
<activity
|
||||
android:name=".activity.support.AccountSelectorActivity"
|
||||
android:label="@string/select_account"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.SELECT_ACCOUNT" />
|
||||
<action android:name="org.mariotaku.twidere.SELECT_ACCOUNT"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -254,34 +254,34 @@
|
|||
android:exported="false"
|
||||
android:label="@string/browser">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.VIEW_WEBPAGE" />
|
||||
<action android:name="org.mariotaku.twidere.VIEW_WEBPAGE"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="file"/>
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.support.ColorPickerDialogActivity"
|
||||
android:label="@string/set_color"
|
||||
android:theme="@style/Theme.Twidere.Light.NoDisplay" />
|
||||
android:theme="@style/Theme.Twidere.Light.NoDisplay"/>
|
||||
<activity
|
||||
android:name=".activity.support.LinkHandlerActivity"
|
||||
android:theme="@style/Theme.Twidere.Dark.DialogWhenLarge.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.support.HomeActivity" />
|
||||
android:value=".activity.support.HomeActivity"/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<data android:scheme="twidere" />
|
||||
<data android:scheme="twidere"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -292,14 +292,14 @@
|
|||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.Twidere.Viewer">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.VIEW_MEDIA" />
|
||||
<action android:name="org.mariotaku.twidere.VIEW_MEDIA"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
<data android:scheme="file"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -309,10 +309,10 @@
|
|||
android:theme="@style/Theme.Twidere.Light.NoDisplay"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.PICK_FILE" />
|
||||
<action android:name="org.mariotaku.twidere.PICK_DIRECTORY" />
|
||||
<action android:name="org.mariotaku.twidere.PICK_FILE"/>
|
||||
<action android:name="org.mariotaku.twidere.PICK_DIRECTORY"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -321,11 +321,11 @@
|
|||
android:theme="@style/Theme.Twidere.Light.NoDisplay"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="org.mariotaku.twidere.PICK_IMAGE" />
|
||||
<action android:name="org.mariotaku.twidere.TAKE_PHOTO" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="org.mariotaku.twidere.PICK_IMAGE"/>
|
||||
<action android:name="org.mariotaku.twidere.TAKE_PHOTO"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -335,10 +335,10 @@
|
|||
android:theme="@style/Theme.Twidere.Dark.Dialog"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.ADD_TAB" />
|
||||
<action android:name="org.mariotaku.twidere.EDIT_TAB" />
|
||||
<action android:name="org.mariotaku.twidere.ADD_TAB"/>
|
||||
<action android:name="org.mariotaku.twidere.EDIT_TAB"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -346,9 +346,9 @@
|
|||
android:label="@string/compose"
|
||||
android:theme="@style/Theme.Twidere.Dark.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -356,18 +356,18 @@
|
|||
android:label="@string/permissions_request"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.REQUEST_PERMISSIONS" />
|
||||
<action android:name="org.mariotaku.twidere.REQUEST_PERMISSIONS"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.CameraCropActivity"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.CAMERA_CROP" />
|
||||
<action android:name="org.mariotaku.twidere.CAMERA_CROP"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -375,31 +375,31 @@
|
|||
android:label="@string/select_user_list"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.SELECT_USER" />
|
||||
<action android:name="org.mariotaku.twidere.SELECT_USER_LIST" />
|
||||
<action android:name="org.mariotaku.twidere.SELECT_USER"/>
|
||||
<action android:name="org.mariotaku.twidere.SELECT_USER_LIST"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SettingsWizardActivity"
|
||||
android:label="@string/settings_wizard"
|
||||
android:theme="@style/Theme.Twidere.Wizard" />
|
||||
android:theme="@style/Theme.Twidere.Wizard"/>
|
||||
<activity
|
||||
android:name=".activity.support.DataExportActivity"
|
||||
android:label="@string/export_settings"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
android:theme="@android:style/Theme.NoDisplay"/>
|
||||
<activity
|
||||
android:name=".activity.support.DataImportActivity"
|
||||
android:label="@string/import_settings"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
android:theme="@android:style/Theme.NoDisplay"/>
|
||||
<activity
|
||||
android:name=".activity.support.ActivityPickerActivity"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.PICK_ACTIVITY" />
|
||||
<action android:name="org.mariotaku.twidere.PICK_ACTIVITY"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -411,33 +411,33 @@
|
|||
<data
|
||||
android:host="twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="http" />
|
||||
android:scheme="http"/>
|
||||
<data
|
||||
android:host="twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="https" />
|
||||
android:scheme="https"/>
|
||||
<data
|
||||
android:host="www.twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="http" />
|
||||
android:scheme="http"/>
|
||||
<data
|
||||
android:host="www.twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="https" />
|
||||
android:scheme="https"/>
|
||||
<data
|
||||
android:host="mobile.twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="http" />
|
||||
android:scheme="http"/>
|
||||
<data
|
||||
android:host="mobile.twitter.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="https" />
|
||||
android:scheme="https"/>
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
|
@ -447,18 +447,18 @@
|
|||
android:taskAffinity=":twidere_assist_launcher"
|
||||
android:theme="@style/Theme.Launcher">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ASSIST" />
|
||||
<action android:name="android.intent.action.ASSIST"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="com.android.systemui.action_assist_icon"
|
||||
android:resource="@drawable/ic_assist_twidere" />
|
||||
android:resource="@drawable/ic_assist_twidere"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.KeyboardShortcutPreferenceCompatActivity"
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog" />
|
||||
android:theme="@style/Theme.Twidere.Dark.Dialog"/>
|
||||
<activity
|
||||
android:name=".activity.TestActivity"
|
||||
android:enabled="false"
|
||||
|
@ -466,27 +466,30 @@
|
|||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.NyanActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.Nyan"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
android:windowSoftInputMode="stateAlwaysHidden"/>
|
||||
<activity
|
||||
android:name=".activity.UsageStatisticsActivity"
|
||||
android:label="@string/usage_statistics"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
android:theme="@android:style/Theme.NoDisplay"/>
|
||||
|
||||
<service
|
||||
android:name=".service.RefreshService"
|
||||
android:label="@string/label_refresh_service" />
|
||||
android:label="@string/label_refresh_service"/>
|
||||
<service
|
||||
android:name=".service.StreamingService"
|
||||
android:label="@string/label_streaming_service"/>
|
||||
<service
|
||||
android:name=".service.BackgroundOperationService"
|
||||
android:label="@string/label_background_operation_service" />
|
||||
android:label="@string/label_background_operation_service"/>
|
||||
<service
|
||||
android:name=".nyan.NyanWallpaperService"
|
||||
android:enabled="false"
|
||||
|
@ -496,12 +499,12 @@
|
|||
android:permission="android.permission.BIND_WALLPAPER"
|
||||
android:process=":wallpaper">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.service.wallpaper.WallpaperService" />
|
||||
<action android:name="android.service.wallpaper.WallpaperService"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.service.wallpaper"
|
||||
android:resource="@xml/nyan_wallpaper" />
|
||||
android:resource="@xml/nyan_wallpaper"/>
|
||||
</service>
|
||||
<service
|
||||
android:name=".nyan.NyanDaydreamService"
|
||||
|
@ -512,7 +515,7 @@
|
|||
android:process=":daydream"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.service.dreams.DreamService" />
|
||||
<action android:name="android.service.dreams.DreamService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
@ -522,53 +525,53 @@
|
|||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
android:label="@string/label_data_provider"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
tools:ignore="ExportedContentProvider"/>
|
||||
<provider
|
||||
android:name=".provider.TwidereCommandProvider"
|
||||
android:authorities="twidere.command"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
tools:ignore="ExportedContentProvider"/>
|
||||
<provider
|
||||
android:name=".provider.RecentSearchProvider"
|
||||
android:authorities="org.mariotaku.twidere.provider.SearchRecentSuggestions"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
tools:ignore="ExportedContentProvider"/>
|
||||
|
||||
<receiver android:name=".receiver.ConnectivityStateReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receiver.NotificationReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="org.mariotaku.twidere.NOTIFICATION_DELETED" />
|
||||
<action android:name="org.mariotaku.twidere.NOTIFICATION_DELETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receiver.SecretCodeBroadcastReceiver"
|
||||
android:label="@string/twidere_test">
|
||||
<intent-filter>
|
||||
<action android:name="android.provider.Telephony.SECRET_CODE" />
|
||||
<action android:name="android.provider.Telephony.SECRET_CODE"/>
|
||||
|
||||
<data
|
||||
android:host="8943373"
|
||||
android:scheme="android_secret_code" />
|
||||
android:scheme="android_secret_code"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Begin third Party components -->
|
||||
|
||||
<!-- SPICE -->
|
||||
<service android:name="edu.tsinghua.spice.SpiceService" />
|
||||
<service android:name="edu.tsinghua.spice.SpiceService"/>
|
||||
<receiver
|
||||
android:name="edu.tsinghua.spice.SpiceUploadReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
|
||||
<action android:name="edu.tsinghua.spice.UPLOAD_PROFILE" />
|
||||
<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||
<action android:name="edu.tsinghua.spice.UPLOAD_PROFILE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package edu.ucdavis.earlybird;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public final class CSVFileFilter implements FileFilter {
|
||||
|
||||
@Override
|
||||
public boolean accept(final File file) {
|
||||
return file.isFile() && "csv".equalsIgnoreCase(getExtension(file));
|
||||
}
|
||||
|
||||
static String getExtension(final File file) {
|
||||
final String name = file.getName();
|
||||
final int pos = name.lastIndexOf('.');
|
||||
if (pos == -1) return null;
|
||||
return name.substring(pos + 1);
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package edu.ucdavis.earlybird;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
public class ProfilingUtil {
|
||||
|
||||
public static final String FILE_NAME_PROFILE = "Profile";
|
||||
public static final String FILE_NAME_LOCATION = "Location";
|
||||
public static final String FILE_NAME_APP = "App";
|
||||
public static final String FILE_NAME_WIFI = "Wifi";
|
||||
|
||||
public static boolean isCharging(final Context context) {
|
||||
if (context == null) return false;
|
||||
final Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
if (intent == null) return false;
|
||||
final int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
|
||||
return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
|
||||
}
|
||||
|
||||
public static boolean log(final Context context, final String msg) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
final StackTraceElement ste = new Throwable().fillInStackTrace().getStackTrace()[1];
|
||||
final String fullname = ste.getClassName();
|
||||
final String name = fullname.substring(fullname.lastIndexOf('.'));
|
||||
final String tag = name + "." + ste.getMethodName();
|
||||
Log.d(tag, msg);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void profile(final Context context, final long accountID, final String text) {
|
||||
profile(context, accountID + "_" + FILE_NAME_PROFILE, text);
|
||||
}
|
||||
|
||||
public static void profile(final Context context, final String name, final String text) {
|
||||
if (context == null) return;
|
||||
final SharedPreferences prefs = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
if (!prefs.getBoolean(Constants.KEY_USAGE_STATISTICS, false)) return;
|
||||
final String filename = name + ".csv";
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final FileOutputStream fos = context.openFileOutput(filename, Context.MODE_APPEND);
|
||||
if (fos == null) return;
|
||||
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
|
||||
bw.write("[" + System.currentTimeMillis() + "], " + text + "\n");
|
||||
bw.flush();
|
||||
fos.close();
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package edu.ucdavis.earlybird;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* Request location ONCE per WAKE_PERIOD_IN_MILLI.
|
||||
*/
|
||||
public class UCDService extends Service {
|
||||
|
||||
public static final String ACTION_GET_LOCATION = "edu.ucdavis.earlybird.GET_LOCATION";
|
||||
private AlarmManager mAlarmManager;
|
||||
private PendingIntent uploadIntent;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(final Intent intent) {
|
||||
throw new IllegalStateException("Not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
ProfilingUtil.log(this, "onCreate");
|
||||
mAlarmManager = (AlarmManager) getSystemService(Service.ALARM_SERVICE);
|
||||
|
||||
// Upload Service
|
||||
final Intent i = new Intent(UploadReceiver.ACTION_UPLOAD_PROFILE);
|
||||
uploadIntent = PendingIntent.getBroadcast(this, 0, i, 0);
|
||||
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 12 * 60 * 60 * 1000, uploadIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mAlarmManager.cancel(uploadIntent);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package edu.ucdavis.earlybird;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
|
||||
public class UploadReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String ACTION_UPLOAD_PROFILE = "edu.ucdavis.earlybird.UPLOAD_PROFILE";
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
final boolean isWifi = Utils.isOnWifi(context.getApplicationContext());
|
||||
final boolean isCharging = ProfilingUtil.isCharging(context.getApplicationContext());
|
||||
if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) {
|
||||
final boolean wifi = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
|
||||
ProfilingUtil.profile(context, ProfilingUtil.FILE_NAME_WIFI, wifi ? "connected" : "disconnected");
|
||||
}
|
||||
if (isWifi && isCharging) {
|
||||
new UploadTask(context).execute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
package edu.ucdavis.earlybird;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.Settings.Secure;
|
||||
|
||||
import org.mariotaku.restfu.annotation.method.POST;
|
||||
import org.mariotaku.restfu.http.RestHttpClient;
|
||||
import org.mariotaku.restfu.http.RestHttpRequest;
|
||||
import org.mariotaku.restfu.http.RestHttpResponse;
|
||||
import org.mariotaku.restfu.http.mime.FileTypedData;
|
||||
import org.mariotaku.restfu.http.mime.MultipartTypedBody;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.mariotaku.twidere.util.Utils.copyStream;
|
||||
|
||||
public class UploadTask extends AsyncTask<Object, Object, Object> {
|
||||
|
||||
private static final String LAST_UPLOAD_DATE = "last_upload_time";
|
||||
private static final double MILLSECS_HALF_DAY = 1000 * 60 * 60 * 12;
|
||||
|
||||
private final String device_id;
|
||||
private final Context context;
|
||||
|
||||
private final RestHttpClient client;
|
||||
|
||||
private static final String PROFILE_SERVER_URL = "http://weik.metaisle.com/profiles";
|
||||
|
||||
// private static final String PROFILE_SERVER_URL =
|
||||
// "http://192.168.0.105:3000/profiles";
|
||||
|
||||
public UploadTask(final Context context) {
|
||||
this.context = context;
|
||||
this.client = TwitterAPIFactory.getDefaultHttpClient(context);
|
||||
device_id = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
|
||||
}
|
||||
|
||||
public void uploadMultipart(final String url, final File file) {
|
||||
final String app_root = file.getParent();
|
||||
final File tmp_dir = new File(app_root + "/tmp");
|
||||
if (!tmp_dir.exists()) {
|
||||
if (!tmp_dir.mkdirs()) {
|
||||
ProfilingUtil.log(context, "cannot create tmp, do nothing.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
final File tmp = new File(tmp_dir, file.getName());
|
||||
file.renameTo(tmp);
|
||||
|
||||
try {
|
||||
|
||||
final RestHttpRequest.Builder builder = new RestHttpRequest.Builder();
|
||||
builder.url(PROFILE_SERVER_URL);
|
||||
builder.method(POST.METHOD);
|
||||
final MultipartTypedBody body = new MultipartTypedBody();
|
||||
body.add("upload", new FileTypedData(tmp));
|
||||
builder.body(body);
|
||||
final RestHttpResponse response = client.execute(builder.build());
|
||||
|
||||
// Responses from the server (code and message)
|
||||
final int serverResponseCode = response.getStatus();
|
||||
|
||||
ProfilingUtil.log(context, "server response code " + serverResponseCode);
|
||||
|
||||
if (serverResponseCode / 100 == 2) {
|
||||
tmp.delete();
|
||||
} else {
|
||||
putBackProfile(context, tmp, file);
|
||||
}
|
||||
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
putBackProfile(context, tmp, file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(final Object... params) {
|
||||
|
||||
final SharedPreferences prefs = context.getSharedPreferences("ucd_data_profiling", Context.MODE_PRIVATE);
|
||||
|
||||
if (prefs.contains(LAST_UPLOAD_DATE)) {
|
||||
final long lastUpload = prefs.getLong(LAST_UPLOAD_DATE, System.currentTimeMillis());
|
||||
final double deltaDays = (System.currentTimeMillis() - lastUpload) / (MILLSECS_HALF_DAY * 2);
|
||||
if (deltaDays < 1) {
|
||||
ProfilingUtil.log(context, "Uploaded less than 1 day ago.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final File root = context.getFilesDir();
|
||||
final File[] files = root.listFiles(new CSVFileFilter());
|
||||
|
||||
uploadToNode(files);
|
||||
prefs.edit().putLong(LAST_UPLOAD_DATE, System.currentTimeMillis()).apply();
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean uploadToNode(final File... files) {
|
||||
for (final File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
final String url = PROFILE_SERVER_URL + "/" + device_id + "/"
|
||||
+ file.getName().replaceFirst("[.][^.]+$", "");
|
||||
ProfilingUtil.log(context, url);
|
||||
uploadMultipart(url, file);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void putBackProfile(final Context context, final File tmp, final File profile) {
|
||||
boolean success;
|
||||
if (profile.exists()) {
|
||||
try {
|
||||
final FileOutputStream os = new FileOutputStream(tmp, true);
|
||||
final FileInputStream is = new FileInputStream(profile);
|
||||
copyStream(is, os);
|
||||
is.close();
|
||||
os.close();
|
||||
success = true;
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success && tmp.renameTo(profile) && tmp.delete()) {
|
||||
ProfilingUtil.log(context, "put profile back success");
|
||||
} else {
|
||||
ProfilingUtil.log(context, "put profile back failed");
|
||||
}
|
||||
} else {
|
||||
if (tmp.renameTo(profile)) {
|
||||
ProfilingUtil.log(context, "put profile back success");
|
||||
} else {
|
||||
ProfilingUtil.log(context, "put profile back failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
|||
import org.mariotaku.twidere.util.AsyncTaskUtils;
|
||||
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
import org.mariotaku.twidere.util.TwitterAPIUtils;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -238,7 +237,7 @@ public class BrowserSignInActivity extends BaseSupportDialogActivity {
|
|||
consumerSecret = defConsumerSecret;
|
||||
}
|
||||
try {
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIUtils.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", "oauth"));
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", "oauth"));
|
||||
final Authorization auth = new OAuthAuthorization(consumerKey, consumerSecret);
|
||||
final TwitterOAuth twitter = TwitterAPIFactory.getInstance(mActivity, endpoint, auth, TwitterOAuth.class);
|
||||
return twitter.getRequestToken(OAUTH_CALLBACK_OOB);
|
||||
|
|
|
@ -87,6 +87,7 @@ import org.mariotaku.twidere.model.SupportTabSpec;
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Mentions;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.service.StreamingService;
|
||||
import org.mariotaku.twidere.util.AsyncTaskUtils;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.CustomTabUtils;
|
||||
|
@ -120,7 +121,6 @@ import java.util.Map.Entry;
|
|||
|
||||
import edu.tsinghua.spice.Utilies.NetworkStateUtil;
|
||||
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
||||
import edu.ucdavis.earlybird.ProfilingUtil;
|
||||
|
||||
import static org.mariotaku.twidere.util.CompareUtils.classEquals;
|
||||
import static org.mariotaku.twidere.util.Utils.cleanDatabasesByItemLimit;
|
||||
|
@ -428,6 +428,8 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
|
||||
final int initialTabPosition = handleIntent(intent, savedInstanceState == null);
|
||||
setTabPosition(initialTabPosition);
|
||||
|
||||
startService(new Intent(this, StreamingService.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -440,8 +442,6 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
final Bus bus = TwidereApplication.getInstance(this).getMessageBus();
|
||||
assert bus != null;
|
||||
bus.register(this);
|
||||
// UCD
|
||||
ProfilingUtil.profile(this, ProfilingUtil.FILE_NAME_APP, "App onStart");
|
||||
// spice
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Launch" + "," + Build.MODEL
|
||||
+ "," + "mediaPreview=" + mPreferences.getBoolean(KEY_MEDIA_PREVIEW, false));
|
||||
|
@ -480,8 +480,6 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
mPreferences.edit().putInt(KEY_SAVED_TAB_POSITION, mViewPager.getCurrentItem()).apply();
|
||||
sendBroadcast(new Intent(BROADCAST_HOME_ACTIVITY_ONSTOP));
|
||||
|
||||
// UCD
|
||||
ProfilingUtil.profile(this, ProfilingUtil.FILE_NAME_APP, "App onStop");
|
||||
// spice
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Stop");
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Stop" + "," + NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
||||
|
@ -621,6 +619,9 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
|
||||
stopService(new Intent(this, StreamingService.class));
|
||||
|
||||
// Delete unused items in databases.
|
||||
cleanDatabasesByItemLimit(this);
|
||||
sendBroadcast(new Intent(BROADCAST_HOME_ACTIVITY_ONDESTROY));
|
||||
|
|
|
@ -91,7 +91,6 @@ import org.mariotaku.twidere.util.ThemeUtils;
|
|||
import org.mariotaku.twidere.util.TwidereActionModeForChildListener;
|
||||
import org.mariotaku.twidere.util.TwidereColorUtils;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
import org.mariotaku.twidere.util.TwitterAPIUtils;
|
||||
import org.mariotaku.twidere.util.support.ViewSupport;
|
||||
import org.mariotaku.twidere.util.support.view.ViewOutlineProviderCompat;
|
||||
import org.mariotaku.twidere.view.iface.TintedStatusLayout;
|
||||
|
@ -599,7 +598,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
protected SignInResponse doInBackground(final Object... params) {
|
||||
try {
|
||||
final String versionSuffix = noVersionSuffix ? null : "1.1";
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(context, endpoint,
|
||||
new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret()), TwitterOAuth.class);
|
||||
final OAuthToken accessToken = oauth.getAccessToken(requestToken, oauthVerifier);
|
||||
|
@ -684,7 +683,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
|
||||
private SignInResponse authBasic() throws TwitterException {
|
||||
final String versionSuffix = noVersionSuffix ? null : "1.1";
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Authorization auth = new BasicAuthorization(username, password);
|
||||
final Twitter twitter = TwitterAPIFactory.getInstance(context, endpoint, auth, Twitter.class);
|
||||
final User user = twitter.verifyCredentials();
|
||||
|
@ -698,9 +697,9 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
|
||||
private SignInResponse authOAuth() throws AuthenticationException, TwitterException {
|
||||
String endpointUrl, signEndpointUrl;
|
||||
endpointUrl = TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", null);
|
||||
endpointUrl = TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", null);
|
||||
if (!sameOAuthSigningUrl) {
|
||||
signEndpointUrl = TwitterAPIUtils.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", null);
|
||||
signEndpointUrl = TwitterAPIFactory.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", null);
|
||||
} else {
|
||||
signEndpointUrl = endpointUrl;
|
||||
}
|
||||
|
@ -712,9 +711,9 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
final long userId = accessToken.getUserId();
|
||||
if (userId <= 0) return new SignInResponse(false, false, null);
|
||||
final String versionSuffix = noVersionSuffix ? null : "1.1";
|
||||
endpointUrl = TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", versionSuffix);
|
||||
endpointUrl = TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix);
|
||||
if (!sameOAuthSigningUrl) {
|
||||
signEndpointUrl = TwitterAPIUtils.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", versionSuffix);
|
||||
signEndpointUrl = TwitterAPIFactory.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", versionSuffix);
|
||||
} else {
|
||||
signEndpointUrl = endpointUrl;
|
||||
}
|
||||
|
@ -730,7 +729,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
|
||||
private SignInResponse authTwipOMode() throws TwitterException {
|
||||
final String versionSuffix = noVersionSuffix ? null : "1.1";
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Authorization auth = new EmptyAuthorization();
|
||||
final Twitter twitter = TwitterAPIFactory.getInstance(context, endpoint, auth, Twitter.class);
|
||||
final User user = twitter.verifyCredentials();
|
||||
|
@ -742,7 +741,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
|
|||
|
||||
private SignInResponse authxAuth() throws TwitterException {
|
||||
final String versionSuffix = noVersionSuffix ? null : "1.1";
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIUtils.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
final Endpoint endpoint = new Endpoint(TwitterAPIFactory.getApiUrl(apiUrlFormat, "api", versionSuffix));
|
||||
OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret());
|
||||
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(context, endpoint, auth, TwitterOAuth.class);
|
||||
final OAuthToken accessToken = oauth.getAccessToken(username, password, TwitterOAuth.XAuthMode.CLIENT);
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.mariotaku.twidere.api.twitter;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -42,6 +44,8 @@ import java.io.InputStreamReader;
|
|||
*/
|
||||
public abstract class UserStreamCallback implements RawCallback {
|
||||
|
||||
private boolean connected;
|
||||
|
||||
private boolean disconnected;
|
||||
|
||||
@Override
|
||||
|
@ -54,78 +58,93 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
}
|
||||
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;
|
||||
try {
|
||||
for (String line; (line = reader.readLine()) != null && !disconnected; ) {
|
||||
if (!connected) {
|
||||
onConnected();
|
||||
connected = true;
|
||||
}
|
||||
case STATUS: {
|
||||
onStatus(LoganSquare.mapperFor(Status.class).parse(rootNode.traverse()));
|
||||
break;
|
||||
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: {
|
||||
onUnfavorite(parse(User.class, rootNode.get("source")),
|
||||
parse(User.class, rootNode.get("target")),
|
||||
parse(Status.class, rootNode.get("target_object")));
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
onException(e);
|
||||
} finally {
|
||||
Log.d("Twidere.Stream", "Cleaning up...");
|
||||
reader.close();
|
||||
response.close();
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
@ -139,6 +158,8 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
disconnected = true;
|
||||
}
|
||||
|
||||
public abstract void onConnected();
|
||||
|
||||
public abstract void onBlock(User source, User blockedUser);
|
||||
|
||||
public abstract void onDeletionNotice(long directMessageId, long userId);
|
|
@ -55,6 +55,7 @@ import org.mariotaku.twidere.activity.MainHondaJOJOActivity;
|
|||
import org.mariotaku.twidere.service.RefreshService;
|
||||
import org.mariotaku.twidere.util.AsyncTaskManager;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.DebugModeUtils;
|
||||
import org.mariotaku.twidere.util.ErrorLogger;
|
||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
|
@ -73,7 +74,6 @@ import org.mariotaku.twidere.util.net.TwidereHostAddressResolver;
|
|||
import java.io.File;
|
||||
|
||||
import edu.tsinghua.spice.SpiceService;
|
||||
import edu.ucdavis.earlybird.UCDService;
|
||||
|
||||
import static org.mariotaku.twidere.util.Utils.getBestCacheDir;
|
||||
import static org.mariotaku.twidere.util.Utils.getInternalCacheDir;
|
||||
|
@ -236,6 +236,7 @@ public class TwidereApplication extends MultiDexApplication implements Constants
|
|||
StrictModeUtils.detectAllVmPolicy();
|
||||
}
|
||||
super.onCreate();
|
||||
initDebugMode();
|
||||
initBugReport();
|
||||
mDefaultUserAgent = UserAgentUtils.getDefaultUserAgentString(this);
|
||||
mHandler = new Handler();
|
||||
|
@ -269,6 +270,10 @@ public class TwidereApplication extends MultiDexApplication implements Constants
|
|||
reloadConnectivitySettings();
|
||||
}
|
||||
|
||||
private void initDebugMode() {
|
||||
DebugModeUtils.initForApplication(this);
|
||||
}
|
||||
|
||||
private void initBugReport() {
|
||||
ACRA.init(this);
|
||||
ErrorLogger.setEnabled(BuildConfig.DEBUG);
|
||||
|
@ -313,7 +318,6 @@ public class TwidereApplication extends MultiDexApplication implements Constants
|
|||
|| KEY_PROXY_PORT.equals(key)) {
|
||||
reloadConnectivitySettings();
|
||||
} else if (KEY_USAGE_STATISTICS.equals(key)) {
|
||||
stopService(new Intent(this, UCDService.class));
|
||||
//spice
|
||||
stopService(new Intent(this, SpiceService.class));
|
||||
startUsageStatisticsServiceIfNeeded(this);
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.mariotaku.twidere.R;
|
||||
|
||||
public class AccountRefreshSettingsFragment extends BaseAccountPreferenceFragment {
|
||||
|
@ -34,7 +36,8 @@ public class AccountRefreshSettingsFragment extends BaseAccountPreferenceFragmen
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String getSwitchPreferenceKey() {
|
||||
return KEY_AUTO_REFRESH;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ import android.preference.PreferenceActivity;
|
|||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
@ -78,12 +80,15 @@ public abstract class BaseAccountPreferenceFragment extends PreferenceFragment i
|
|||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.menu_switch_preference, menu);
|
||||
final View actionView = menu.findItem(MENU_TOGGLE).getActionView();
|
||||
final CompoundButton toggle = (CompoundButton) actionView.findViewById(android.R.id.toggle);
|
||||
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
|
||||
toggle.setOnCheckedChangeListener(this);
|
||||
toggle.setChecked(prefs.getBoolean(getSwitchPreferenceKey(), getSwitchPreferenceDefault()));
|
||||
final String switchKey = getSwitchPreferenceKey();
|
||||
if (!TextUtils.isEmpty(switchKey)) {
|
||||
inflater.inflate(R.menu.menu_switch_preference, menu);
|
||||
final View actionView = menu.findItem(MENU_TOGGLE).getActionView();
|
||||
final CompoundButton toggle = (CompoundButton) actionView.findViewById(android.R.id.toggle);
|
||||
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
|
||||
toggle.setOnCheckedChangeListener(this);
|
||||
toggle.setChecked(prefs.getBoolean(switchKey, getSwitchPreferenceDefault()));
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
|
@ -104,6 +109,7 @@ public abstract class BaseAccountPreferenceFragment extends PreferenceFragment i
|
|||
|
||||
protected abstract boolean getSwitchPreferenceDefault();
|
||||
|
||||
@Nullable
|
||||
protected abstract String getSwitchPreferenceKey();
|
||||
|
||||
private void updatePreferenceScreen() {
|
||||
|
|
|
@ -136,6 +136,7 @@ public class AccountsDashboardFragment extends BaseSupportFragment implements Lo
|
|||
private ActionMenuView mAccountsToggleMenu;
|
||||
private View mAccountProfileContainer;
|
||||
private View mNoAccountContainer;
|
||||
private ActionMenuView mActionMenuView;
|
||||
|
||||
private Context mThemedContext;
|
||||
private MediaLoaderWrapper mImageLoader;
|
||||
|
@ -313,30 +314,6 @@ public class AccountsDashboardFragment extends BaseSupportFragment implements Lo
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else if (adapter instanceof AppMenuAdapter) {
|
||||
if (!(item instanceof OptionItem)) return;
|
||||
final OptionItem option = (OptionItem) item;
|
||||
switch (option.id) {
|
||||
case MENU_ACCOUNTS: {
|
||||
Utils.openAccountsManager(getActivity());
|
||||
break;
|
||||
}
|
||||
case MENU_DRAFTS: {
|
||||
Utils.openDrafts(getActivity());
|
||||
break;
|
||||
}
|
||||
case MENU_FILTERS: {
|
||||
Utils.openFilters(getActivity());
|
||||
break;
|
||||
}
|
||||
case MENU_SETTINGS: {
|
||||
final Intent intent = new Intent(getActivity(), SettingsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
startActivityForResult(intent, REQUEST_SETTINGS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closeAccountsDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,11 +408,41 @@ public class AccountsDashboardFragment extends BaseSupportFragment implements Lo
|
|||
|
||||
mAdapter.addView(mAccountSelectorView, true);
|
||||
mAdapter.addAdapter(mAccountOptionsAdapter);
|
||||
mAdapter.addView(mAppMenuSectionView, false);
|
||||
mAdapter.addAdapter(mAppMenuAdapter);
|
||||
// mAdapter.addView(mAppMenuSectionView, false);
|
||||
// mAdapter.addAdapter(mAppMenuAdapter);
|
||||
mListView.setAdapter(mAdapter);
|
||||
mListView.setOnItemClickListener(this);
|
||||
mPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
menuInflater.inflate(R.menu.menu_dashboard, mActionMenuView.getMenu());
|
||||
|
||||
mActionMenuView.setOnMenuItemClickListener(new OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case MENU_ACCOUNTS: {
|
||||
Utils.openAccountsManager(getActivity());
|
||||
return true;
|
||||
}
|
||||
case MENU_DRAFTS: {
|
||||
Utils.openDrafts(getActivity());
|
||||
return true;
|
||||
}
|
||||
case MENU_FILTERS: {
|
||||
Utils.openFilters(getActivity());
|
||||
return true;
|
||||
}
|
||||
case MENU_SETTINGS: {
|
||||
final Intent intent = new Intent(getActivity(), SettingsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
startActivityForResult(intent, REQUEST_SETTINGS);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
ThemeUtils.resetCheatSheet(mActionMenuView);
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
|
@ -448,6 +455,7 @@ public class AccountsDashboardFragment extends BaseSupportFragment implements Lo
|
|||
public void onBaseViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onBaseViewCreated(view, savedInstanceState);
|
||||
mListView = (ListView) view.findViewById(android.R.id.list);
|
||||
mActionMenuView = (ActionMenuView) view.findViewById(R.id.dashboard_menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -849,7 +857,7 @@ public class AccountsDashboardFragment extends BaseSupportFragment implements Lo
|
|||
private final int mActionIconColor;
|
||||
|
||||
public OptionItemsAdapter(final Context context) {
|
||||
super(context, R.layout.list_item_menu);
|
||||
super(context, R.layout.list_item_dashboard_menu);
|
||||
mActionIconColor = ThemeUtils.getThemeForegroundColor(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment.support;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
@ -295,6 +296,16 @@ public class DirectMessagesFragment extends AbsContentRecyclerViewFragment<Messa
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (isVisibleToUser && activity != null) {
|
||||
final NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(NOTIFICATION_ID_MENTIONS_TIMELINE);
|
||||
}
|
||||
}
|
||||
|
||||
protected long getAccountId() {
|
||||
final Bundle args = getArguments();
|
||||
return args != null ? args.getLong(EXTRA_ACCOUNT_ID, -1) : -1;
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment.support;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
|
@ -65,8 +68,19 @@ public class HomeTimelineFragment extends CursorStatusesFragment {
|
|||
return twitter.getHomeTimelineAsync(accountIds, maxIds, sinceIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (isVisibleToUser && activity != null) {
|
||||
final NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(NOTIFICATION_ID_HOME_TIMELINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getReadPositionTag() {
|
||||
return TAB_TYPE_HOME_TIMELINE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
|
||||
package org.mariotaku.twidere.fragment.support;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.mariotaku.twidere.adapter.CursorStatusesAdapter;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Mentions;
|
||||
|
@ -75,6 +77,16 @@ public class MentionsTimelineFragment extends CursorStatusesFragment {
|
|||
return twitter.getMentionsTimelineAsync(accountIds, maxIds, sinceIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (isVisibleToUser && activity != null) {
|
||||
final NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nm.cancel(NOTIFICATION_ID_MENTIONS_TIMELINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getReadPositionTag() {
|
||||
return TAB_TYPE_MENTIONS_TIMELINE;
|
||||
|
|
|
@ -93,6 +93,10 @@ public class AccountPreferences implements Constants {
|
|||
return mPreferences.getBoolean(KEY_AUTO_REFRESH_TRENDS, DEFAULT_AUTO_REFRESH_TRENDS);
|
||||
}
|
||||
|
||||
public boolean isStreamingEnabled() {
|
||||
return mPreferences.getBoolean(KEY_ENABLE_STREAMING, false);
|
||||
}
|
||||
|
||||
public boolean isDirectMessagesNotificationEnabled() {
|
||||
return mPreferences.getBoolean(KEY_DIRECT_MESSAGES_NOTIFICATION, DEFAULT_DIRECT_MESSAGES_NOTIFICATION);
|
||||
}
|
||||
|
|
|
@ -27,16 +27,12 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
|
@ -55,6 +51,7 @@ import java.util.List;
|
|||
public abstract class AccountsListPreference extends PreferenceCategory implements Constants {
|
||||
|
||||
private static final int[] ATTRS = {R.attr.switchKey, R.attr.switchDefault};
|
||||
@Nullable
|
||||
private final String mSwitchKey;
|
||||
private final boolean mSwitchDefault;
|
||||
|
||||
|
@ -77,8 +74,8 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
|
|||
public void setAccountsData(final List<ParcelableAccount> accounts) {
|
||||
removeAll();
|
||||
for (final ParcelableAccount account : accounts) {
|
||||
final AccountItemPreference preference = new AccountItemPreference(getContext(), account, mSwitchKey,
|
||||
mSwitchDefault);
|
||||
final AccountItemPreference preference = new AccountItemPreference(getContext(), account,
|
||||
mSwitchKey, mSwitchDefault);
|
||||
setupPreference(preference, account);
|
||||
addPreference(preference);
|
||||
}
|
||||
|
@ -96,39 +93,23 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
|
|||
protected abstract void setupPreference(AccountItemPreference preference, ParcelableAccount account);
|
||||
|
||||
public static final class AccountItemPreference extends Preference implements ImageLoadingListener,
|
||||
OnCheckedChangeListener, OnSharedPreferenceChangeListener, OnPreferenceClickListener, OnClickListener {
|
||||
OnSharedPreferenceChangeListener {
|
||||
|
||||
private final ParcelableAccount mAccount;
|
||||
private final SharedPreferences mSwitchPreference;
|
||||
private final MediaLoaderWrapper mImageLoader;
|
||||
|
||||
private final String mSwitchKey;
|
||||
private final boolean mSwitchDefault;
|
||||
private CompoundButton mToggle;
|
||||
|
||||
public AccountItemPreference(final Context context, final ParcelableAccount account, final String switchKey,
|
||||
final boolean switchDefault) {
|
||||
super(context);
|
||||
setWidgetLayoutResource(R.layout.preference_widget_account_preference_item);
|
||||
setOnPreferenceClickListener(this);
|
||||
final String switchPreferenceName = ACCOUNT_PREFERENCES_NAME_PREFIX + account.account_id;
|
||||
mAccount = account;
|
||||
mSwitchPreference = context.getSharedPreferences(switchPreferenceName, Context.MODE_PRIVATE);
|
||||
final TwidereApplication app = TwidereApplication.getInstance(context);
|
||||
mImageLoader = app.getMediaLoaderWrapper();
|
||||
mSwitchKey = switchKey;
|
||||
mSwitchDefault = switchDefault;
|
||||
mSwitchPreference.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
|
||||
if (mSwitchKey == null) return;
|
||||
final SharedPreferences.Editor editor = mSwitchPreference.edit();
|
||||
editor.putBoolean(mSwitchKey, isChecked);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingCancelled(final String imageUri, final View view) {
|
||||
// setIcon(R.drawable.ic_profile_image_default);
|
||||
|
@ -164,16 +145,6 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
|
|||
mImageLoader.loadProfileImage(mAccount.profile_image_url, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateView(ViewGroup parent) {
|
||||
final View view = super.onCreateView(parent);
|
||||
view.findViewById(R.id.settings).setOnClickListener(this);
|
||||
final CompoundButton toggle = (CompoundButton) view.findViewById(android.R.id.toggle);
|
||||
toggle.setOnCheckedChangeListener(this);
|
||||
mToggle = toggle;
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull final View view) {
|
||||
super.onBindView(view);
|
||||
|
@ -190,26 +161,6 @@ public abstract class AccountsListPreference extends PreferenceCategory implemen
|
|||
if (summaryView instanceof TextView) {
|
||||
((TextView) summaryView).setSingleLine(true);
|
||||
}
|
||||
final CompoundButton toggle = (CompoundButton) view.findViewById(android.R.id.toggle);
|
||||
if (mSwitchKey != null) {
|
||||
toggle.setChecked(mSwitchPreference.getBoolean(mSwitchKey, mSwitchDefault));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (mToggle != null) {
|
||||
mToggle.toggle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final Context context = getContext();
|
||||
if (!(context instanceof PreferenceActivity)) return;
|
||||
final PreferenceActivity activity = (PreferenceActivity) context;
|
||||
activity.startPreferencePanel(getFragment(), getExtras(), getTitleRes(), getTitle(), null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ import org.jraf.android.backport.switchwidget.SwitchPreference;
|
|||
|
||||
public class AutoFixSwitchPreference extends SwitchPreference {
|
||||
|
||||
private View mCachedView;
|
||||
|
||||
public AutoFixSwitchPreference(final Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.jraf.android.backport.switchwidget.SwitchPreference;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
|
||||
public class DarkLightThemeTogglePreference extends SwitchPreference implements Constants {
|
||||
|
||||
public DarkLightThemeTogglePreference(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public DarkLightThemeTogglePreference(final Context context, final AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.asb_switchPreferenceStyle);
|
||||
}
|
||||
|
||||
public DarkLightThemeTogglePreference(final Context context, final AttributeSet attrs, final int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getPersistedBoolean(final boolean defaultReturnValue) {
|
||||
final SharedPreferences preferences = getSharedPreferences();
|
||||
return ThemeUtils.isDarkTheme(getPersistedString(VALUE_THEME_NAME_TWIDERE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean persistBoolean(final boolean value) {
|
||||
return persistString(value ? VALUE_THEME_NAME_DARK : VALUE_THEME_NAME_TWIDERE);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.mariotaku.twidere.extension.streaming;
|
||||
package org.mariotaku.twidere.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
|
@ -13,14 +12,19 @@ import android.database.ContentObserver;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.mariotaku.restfu.http.Authorization;
|
||||
import org.mariotaku.restfu.http.ContentType;
|
||||
import org.mariotaku.restfu.http.Endpoint;
|
||||
import org.mariotaku.twidere.Twidere;
|
||||
import org.mariotaku.twidere.TwidereSharedPreferences;
|
||||
import org.mariotaku.restfu.http.RestHttpResponse;
|
||||
import org.mariotaku.restfu.http.mime.TypedData;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.activity.SettingsActivity;
|
||||
import org.mariotaku.twidere.api.twitter.TwitterException;
|
||||
import org.mariotaku.twidere.api.twitter.TwitterUserStream;
|
||||
import org.mariotaku.twidere.api.twitter.UserStreamCallback;
|
||||
|
@ -30,7 +34,7 @@ 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.extension.streaming.util.Utils;
|
||||
import org.mariotaku.twidere.model.AccountPreferences;
|
||||
import org.mariotaku.twidere.model.ParcelableAccount;
|
||||
import org.mariotaku.twidere.model.ParcelableCredentials;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
||||
|
@ -38,19 +42,21 @@ import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.Mentions;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.util.ContentValuesCreator;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.TwidereArrayUtils;
|
||||
import org.mariotaku.twidere.util.TwitterAPIUtils;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
public class StreamingService extends Service implements Constants, PrivateConstants {
|
||||
public class StreamingService extends Service implements Constants {
|
||||
|
||||
private static final int NOTIFICATION_SERVICE_STARTED = 1;
|
||||
private static final int NOTIFICATION_REQUEST_PERMISSION = 2;
|
||||
|
||||
private final List<WeakReference<TwidereUserStreamCallback>> mTwitterInstances = new ArrayList<>();
|
||||
private final LongSparseArray<UserStreamCallback> mCallbacks = new LongSparseArray<>();
|
||||
private ContentResolver mResolver;
|
||||
|
||||
private SharedPreferences mPreferences;
|
||||
|
@ -86,11 +92,11 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mPreferences = SharedPreferencesWrapper.getInstance(this, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
mResolver = getContentResolver();
|
||||
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(LOGTAG, "Stream service started.");
|
||||
Log.d(Constants.LOGTAG, "Stream service started.");
|
||||
}
|
||||
initStreaming();
|
||||
mResolver.registerContentObserver(Accounts.CONTENT_URI, true, mAccountChangeObserver);
|
||||
|
@ -101,103 +107,93 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
clearTwitterInstances();
|
||||
mResolver.unregisterContentObserver(mAccountChangeObserver);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(LOGTAG, "Stream service stopped.");
|
||||
Log.d(Constants.LOGTAG, "Stream service stopped.");
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void clearTwitterInstances() {
|
||||
for (final WeakReference<TwidereUserStreamCallback> reference : mTwitterInstances) {
|
||||
final TwidereUserStreamCallback twitter = reference.get();
|
||||
new Thread(new ShutdownStreamTwitterRunnable(twitter)).start();
|
||||
for (int i = 0, j = mCallbacks.size(); i < j; i++) {
|
||||
new Thread(new ShutdownStreamTwitterRunnable(mCallbacks.valueAt(i))).start();
|
||||
}
|
||||
mTwitterInstances.clear();
|
||||
mCallbacks.clear();
|
||||
mNotificationManager.cancel(NOTIFICATION_SERVICE_STARTED);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void initStreaming() {
|
||||
if (!mPreferences.getBoolean(PREFERENCE_KEY_ENABLE_STREAMING, true)) return;
|
||||
final boolean granted;
|
||||
try {
|
||||
granted = Twidere.isPermissionGranted(this);
|
||||
} catch (final SecurityException e) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
if (granted) {
|
||||
final TwidereSharedPreferences prefs = Twidere.getSharedPreferences(this);
|
||||
if (setTwitterInstances(prefs)) {
|
||||
final Intent intent = new Intent(this, SettingsActivity.class);
|
||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
final CharSequence contentTitle = getString(R.string.app_name);
|
||||
final CharSequence contentText = getString(R.string.streaming_service_running);
|
||||
final Notification notification = new Notification();
|
||||
notification.flags = Notification.FLAG_ONGOING_EVENT;
|
||||
notification.icon = R.drawable.ic_stat_twidere;
|
||||
notification.tickerText = getString(R.string.streaming_service_running);
|
||||
notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
|
||||
mNotificationManager.notify(NOTIFICATION_SERVICE_STARTED, notification);
|
||||
} else {
|
||||
mNotificationManager.cancel(NOTIFICATION_SERVICE_STARTED);
|
||||
}
|
||||
} else {
|
||||
final Intent intent = new Intent(this, SettingsActivity.class);
|
||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
final CharSequence contentTitle = getString(R.string.app_name);
|
||||
final CharSequence contentText = getString(R.string.request_permission);
|
||||
final Notification notification = new Notification();
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
notification.icon = R.drawable.ic_stat_login;
|
||||
notification.tickerText = getString(R.string.request_permission);
|
||||
notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
|
||||
mNotificationManager.notify(NOTIFICATION_REQUEST_PERMISSION, notification);
|
||||
}
|
||||
if (!BuildConfig.DEBUG) return;
|
||||
setTwitterInstances();
|
||||
updateStreamState();
|
||||
}
|
||||
|
||||
|
||||
private boolean setTwitterInstances(final TwidereSharedPreferences prefs) {
|
||||
if (prefs == null) return false;
|
||||
private boolean setTwitterInstances() {
|
||||
final List<ParcelableCredentials> accountsList = ParcelableAccount.getCredentialsList(this, true);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(LOGTAG, "Setting up twitter stream instances");
|
||||
final long[] accountIds = new long[accountsList.size()];
|
||||
for (int i = 0, j = accountIds.length; i < j; i++) {
|
||||
accountIds[i] = accountsList.get(i).account_id;
|
||||
}
|
||||
mAccountIds = new long[accountsList.size()];
|
||||
final AccountPreferences[] activitedPreferences = AccountPreferences.getAccountPreferences(this, accountIds);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(Constants.LOGTAG, "Setting up twitter stream instances");
|
||||
}
|
||||
mAccountIds = accountIds;
|
||||
clearTwitterInstances();
|
||||
boolean result = false;
|
||||
for (int i = 0, j = accountsList.size(); i < j; i++) {
|
||||
final AccountPreferences preferences = activitedPreferences[i];
|
||||
if (!preferences.isStreamingEnabled()) continue;
|
||||
final ParcelableCredentials account = accountsList.get(i);
|
||||
final Endpoint endpoint = TwitterAPIUtils.getEndpoint(account, TwitterUserStream.class);
|
||||
final Authorization authorization = TwitterAPIUtils.getAuthorization(account);
|
||||
final TwitterUserStream twitter = Utils.getInstance(this, endpoint, authorization, TwitterUserStream.class);
|
||||
final long account_id = account.account_id;
|
||||
mAccountIds[i] = account_id;
|
||||
final Endpoint endpoint = TwitterAPIFactory.getEndpoint(account, TwitterUserStream.class);
|
||||
final Authorization authorization = TwitterAPIFactory.getAuthorization(account);
|
||||
final TwitterUserStream twitter = TwitterAPIFactory.getInstance(this, endpoint, authorization, TwitterUserStream.class);
|
||||
final TwidereUserStreamCallback callback = new TwidereUserStreamCallback(this, account);
|
||||
mTwitterInstances.add(new WeakReference<>(callback));
|
||||
mCallbacks.put(account.account_id, callback);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
twitter.getUserStream(callback);
|
||||
Log.d(LOGTAG, "Stream disconnected");
|
||||
Log.d(Constants.LOGTAG, String.format("Stream %d disconnected", account.account_id));
|
||||
mCallbacks.remove(account.account_id);
|
||||
updateStreamState();
|
||||
}
|
||||
}.start();
|
||||
result |= true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateStreamState() {
|
||||
if (mCallbacks.size() > 0) {
|
||||
final Intent intent = new Intent(this, SettingsActivity.class);
|
||||
final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
final CharSequence contentTitle = getString(R.string.app_name);
|
||||
final CharSequence contentText = getString(R.string.timeline_streaming_running);
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setOngoing(true);
|
||||
builder.setSmallIcon(R.drawable.ic_stat_refresh);
|
||||
builder.setContentTitle(contentTitle);
|
||||
builder.setContentText(contentText);
|
||||
builder.setContentIntent(contentIntent);
|
||||
mNotificationManager.notify(NOTIFICATION_SERVICE_STARTED, builder.build());
|
||||
} else {
|
||||
mNotificationManager.cancel(NOTIFICATION_SERVICE_STARTED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static class ShutdownStreamTwitterRunnable implements Runnable {
|
||||
private final TwidereUserStreamCallback twitter;
|
||||
private final UserStreamCallback callback;
|
||||
|
||||
ShutdownStreamTwitterRunnable(final TwidereUserStreamCallback twitter) {
|
||||
this.twitter = twitter;
|
||||
ShutdownStreamTwitterRunnable(final UserStreamCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (twitter == null) return;
|
||||
Log.d(LOGTAG, "Disconnecting stream");
|
||||
twitter.disconnect();
|
||||
if (callback == null) return;
|
||||
Log.d(Constants.LOGTAG, "Disconnecting stream");
|
||||
callback.disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -216,10 +212,15 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
resolver = context.getContentResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlock(final User source, final User blockedUser) {
|
||||
final String message = String.format("%s blocked %s", source.getScreenName(), blockedUser.getScreenName());
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -259,7 +260,7 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
final ContentValues values = ContentValuesCreator.createDirectMessage(directMessage,
|
||||
account.account_id, false);
|
||||
final Uri.Builder builder = DirectMessages.Inbox.CONTENT_URI.buildUpon();
|
||||
builder.appendQueryParameter(Twidere.QUERY_PARAM_NOTIFY, "true");
|
||||
builder.appendQueryParameter(QUERY_PARAM_NOTIFY, "true");
|
||||
if (values != null) {
|
||||
resolver.insert(builder.build(), values);
|
||||
}
|
||||
|
@ -271,8 +272,35 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
public void onException(final Throwable ex) {
|
||||
if (ex instanceof TwitterException) {
|
||||
Log.w(LOGTAG, String.format("Error %d", ((TwitterException) ex).getStatusCode()), ex);
|
||||
final RestHttpResponse response = ((TwitterException) ex).getHttpResponse();
|
||||
if (response != null) {
|
||||
try {
|
||||
final TypedData body = response.getBody();
|
||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
body.writeTo(os);
|
||||
final String charsetName;
|
||||
if (body != null) {
|
||||
final ContentType contentType = body.contentType();
|
||||
if (contentType != null) {
|
||||
final Charset charset = contentType.getCharset();
|
||||
if (charset != null) {
|
||||
charsetName = charset.name();
|
||||
} else {
|
||||
charsetName = Charset.defaultCharset().name();
|
||||
}
|
||||
} else {
|
||||
charsetName = Charset.defaultCharset().name();
|
||||
}
|
||||
} else {
|
||||
charsetName = Charset.defaultCharset().name();
|
||||
}
|
||||
Log.w(LOGTAG, os.toString(charsetName));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(LOGTAG, ex);
|
||||
Log.w(Constants.LOGTAG, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,14 +308,14 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
public void onFavorite(final User source, final User target, final Status favoritedStatus) {
|
||||
final String message = String.format("%s favorited %s's tweet: %s", source.getScreenName(),
|
||||
target.getScreenName(), favoritedStatus.getText());
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFollow(final User source, final User followedUser) {
|
||||
final String message = String
|
||||
.format("%s followed %s", source.getScreenName(), followedUser.getScreenName());
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -339,15 +367,14 @@ public class StreamingService extends Service implements Constants, PrivateConst
|
|||
public void onUnblock(final User source, final User unblockedUser) {
|
||||
final String message = String.format("%s unblocked %s", source.getScreenName(),
|
||||
unblockedUser.getScreenName());
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnfavorite(final User source, final User target, final Status unfavoritedStatus) {
|
||||
final String message = String.format("%s unfavorited %s's tweet: %s", source.getScreenName(),
|
||||
target.getScreenName(), unfavoritedStatus.getText());
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -105,7 +105,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
|
||||
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
||||
import edu.tsinghua.spice.Utilies.TypeMappingUtil;
|
||||
import edu.ucdavis.earlybird.ProfilingUtil;
|
||||
|
||||
import static org.mariotaku.twidere.provider.TwidereDataStore.STATUSES_URIS;
|
||||
import static org.mariotaku.twidere.util.ContentValuesCreator.createDirectMessage;
|
||||
|
@ -2207,8 +2206,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
rowsDeleted = 0;
|
||||
}
|
||||
countCur.close();
|
||||
// UCD
|
||||
ProfilingUtil.profile(mContext, accountId, "Download tweets, " + TwidereArrayUtils.toString(statusIds, ',', true));
|
||||
//spice
|
||||
SpiceProfilingUtil.profile(mContext, accountId, accountId + ",Refresh," + TwidereArrayUtils.toString(statusIds, ',', true));
|
||||
//end
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener;
|
|||
|
||||
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
||||
import edu.tsinghua.spice.Utilies.TypeMappingUtil;
|
||||
import edu.ucdavis.earlybird.ProfilingUtil;
|
||||
|
||||
import static org.mariotaku.twidere.util.Utils.openStatus;
|
||||
import static org.mariotaku.twidere.util.Utils.openTweetSearch;
|
||||
|
@ -57,8 +56,6 @@ public class OnLinkClickHandler implements OnLinkClickListener, Constants {
|
|||
final boolean sensitive, int start, int end) {
|
||||
if (manager != null && manager.isActive()) return;
|
||||
if (!isPrivateData()) {
|
||||
// UCD
|
||||
ProfilingUtil.profile(context, accountId, "Click, " + link + ", " + type);
|
||||
//spice
|
||||
SpiceProfilingUtil.profile(context, accountId, accountId + ",Visit," + link + "," + TypeMappingUtil.getLinkType(type));
|
||||
//end
|
||||
|
|
|
@ -1111,6 +1111,10 @@ public class ThemeUtils implements Constants {
|
|||
return context.getResources().getColor(R.color.background_color_action_bar_dark);
|
||||
}
|
||||
|
||||
public static boolean isDarkTheme(final String name) {
|
||||
return VALUE_THEME_NAME_DARK.equals(name);
|
||||
}
|
||||
|
||||
public static final class ActionBarContextThemeWrapper extends android.support.v7.internal.view.ContextThemeWrapper {
|
||||
|
||||
public ActionBarContextThemeWrapper(Context base, int themeres) {
|
||||
|
|
|
@ -2,19 +2,43 @@ package org.mariotaku.twidere.util;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.internal.Internal;
|
||||
|
||||
import org.mariotaku.restfu.ExceptionFactory;
|
||||
import org.mariotaku.restfu.HttpRequestFactory;
|
||||
import org.mariotaku.restfu.RequestInfoFactory;
|
||||
import org.mariotaku.restfu.RestAPIFactory;
|
||||
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.RestHttpClient;
|
||||
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.app.TwidereApplication;
|
||||
import org.mariotaku.twidere.model.ConsumerKeyType;
|
||||
|
@ -24,7 +48,13 @@ import org.mariotaku.twidere.util.net.OkHttpRestClient;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
|
@ -109,29 +139,237 @@ public class TwitterAPIFactory implements TwidereConstants {
|
|||
final String consumerSecret = ((OAuthAuthorization) auth).getConsumerSecret();
|
||||
final ConsumerKeyType officialKeyType = TwitterContentUtils.getOfficialKeyType(context, consumerKey, consumerSecret);
|
||||
if (officialKeyType != ConsumerKeyType.UNKNOWN) {
|
||||
userAgent = TwitterAPIUtils.getUserAgentName(officialKeyType);
|
||||
userAgent = getUserAgentName(officialKeyType);
|
||||
} else {
|
||||
userAgent = TwitterAPIUtils.getTwidereUserAgent(context);
|
||||
userAgent = getTwidereUserAgent(context);
|
||||
}
|
||||
} else {
|
||||
userAgent = TwitterAPIUtils.getTwidereUserAgent(context);
|
||||
userAgent = getTwidereUserAgent(context);
|
||||
}
|
||||
factory.setClient(getDefaultHttpClient(context));
|
||||
factory.setConverter(new TwitterConverter());
|
||||
factory.setEndpoint(endpoint);
|
||||
factory.setAuthorization(auth);
|
||||
factory.setRequestInfoFactory(new TwitterAPIUtils.TwidereRequestInfoFactory());
|
||||
factory.setHttpRequestFactory(new TwitterAPIUtils.TwidereHttpRequestFactory(userAgent));
|
||||
factory.setExceptionFactory(new TwitterAPIUtils.TwidereExceptionFactory());
|
||||
factory.setRequestInfoFactory(new TwidereRequestInfoFactory());
|
||||
factory.setHttpRequestFactory(new TwidereHttpRequestFactory(userAgent));
|
||||
factory.setExceptionFactory(new TwidereExceptionFactory());
|
||||
return factory.build(cls);
|
||||
}
|
||||
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint, final ParcelableCredentials credentials, Class<T> cls) {
|
||||
return TwitterAPIFactory.getInstance(context, endpoint, TwitterAPIUtils.getAuthorization(credentials), cls);
|
||||
return TwitterAPIFactory.getInstance(context, endpoint, getAuthorization(credentials), cls);
|
||||
}
|
||||
|
||||
static <T> T getInstance(final Context context, final ParcelableCredentials credentials, final Class<T> cls) {
|
||||
if (credentials == null) return null;
|
||||
return TwitterAPIFactory.getInstance(context, TwitterAPIUtils.getEndpoint(credentials, cls), credentials, cls);
|
||||
return TwitterAPIFactory.getInstance(context, getEndpoint(credentials, cls), credentials, cls);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,7 +252,6 @@ import java.util.zip.CRC32;
|
|||
import javax.net.ssl.SSLException;
|
||||
|
||||
import edu.tsinghua.spice.SpiceService;
|
||||
import edu.ucdavis.earlybird.UCDService;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
import static android.text.format.DateUtils.getRelativeTimeSpanString;
|
||||
|
@ -3418,15 +3417,12 @@ public final class Utils implements Constants {
|
|||
|
||||
public static void startUsageStatisticsServiceIfNeeded(final Context context) {
|
||||
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
final Intent profilingServiceIntent = new Intent(context, UCDService.class);
|
||||
//spice
|
||||
final Intent spiceProfilingServiceIntent = new Intent(context, SpiceService.class);
|
||||
if (prefs.getBoolean(KEY_USAGE_STATISTICS, false)) {
|
||||
context.startService(profilingServiceIntent);
|
||||
//spice
|
||||
context.startService(spiceProfilingServiceIntent);
|
||||
} else {
|
||||
context.stopService(profilingServiceIntent);
|
||||
//spice
|
||||
context.stopService(spiceProfilingServiceIntent);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ import org.mariotaku.twidere.model.ParcelableMedia;
|
|||
import org.mariotaku.twidere.util.MediaPreviewUtils;
|
||||
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
|
||||
import org.mariotaku.twidere.util.TwidereLinkify;
|
||||
import org.mariotaku.twidere.util.TwitterAPIUtils;
|
||||
import org.mariotaku.twidere.util.TwitterAPIFactory;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
|
||||
|
@ -129,7 +128,7 @@ public class TwidereImageDownloader extends BaseImageDownloader implements Const
|
|||
final String host = uri.getHost();
|
||||
final String domain = host.substring(0, host.lastIndexOf(".twitter.com"));
|
||||
final String path = uri.getPath();
|
||||
sb.append(TwitterAPIUtils.getApiUrl(apiUrlFormat, domain, path));
|
||||
sb.append(TwitterAPIFactory.getApiUrl(apiUrlFormat, domain, path));
|
||||
final String query = uri.getQuery();
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
sb.append("?");
|
||||
|
@ -152,7 +151,7 @@ public class TwidereImageDownloader extends BaseImageDownloader implements Const
|
|||
if (isTwitterAuthRequired(uri) && extras instanceof AccountExtra) {
|
||||
final AccountExtra accountExtra = (AccountExtra) extras;
|
||||
account = ParcelableAccount.getCredentials(mContext, accountExtra.account_id);
|
||||
auth = TwitterAPIUtils.getAuthorization(account);
|
||||
auth = TwitterAPIFactory.getAuthorization(account);
|
||||
} else {
|
||||
account = null;
|
||||
auth = null;
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.mariotaku.restfu.http.RestHttpClient;
|
|||
import org.mariotaku.restfu.http.RestHttpRequest;
|
||||
import org.mariotaku.restfu.http.RestHttpResponse;
|
||||
import org.mariotaku.restfu.http.mime.TypedData;
|
||||
import org.mariotaku.twidere.util.DebugModeUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -60,6 +61,7 @@ public class OkHttpRestClient implements RestHttpClient {
|
|||
|
||||
public OkHttpRestClient(OkHttpClient client) {
|
||||
this.client = client;
|
||||
DebugModeUtils.initForHttpClient(client);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<Space xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"/>
|
After Width: | Height: | Size: 718 B |
After Width: | Height: | Size: 510 B |
After Width: | Height: | Size: 853 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
|
@ -18,12 +18,29 @@
|
|||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ListView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="true"
|
||||
android:listSelector="?selectableItemBackground"
|
||||
tools:context=".fragment.support.AccountsDashboardFragment"/>
|
||||
android:divider="?dividerVertical"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="middle">
|
||||
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:divider="@null"
|
||||
android:dividerHeight="0dp"
|
||||
android:focusable="true"
|
||||
android:listSelector="?selectableItemBackground"
|
||||
tools:context=".fragment.support.AccountsDashboardFragment"/>
|
||||
|
||||
<android.support.v7.widget.ActionMenuView
|
||||
android:id="@+id/dashboard_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:layout_weight="0"/>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?listPreferredItemHeightSmall"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_weight="0"
|
||||
android:contentDescription="@string/icon"
|
||||
android:scaleType="centerInside"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -22,7 +22,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="48dp"
|
||||
android:minHeight="?listPreferredItemHeightSmall"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="AlwaysShowAction">
|
||||
<item
|
||||
android:id="@id/accounts"
|
||||
android:layout_weight="0"
|
||||
android:icon="@drawable/ic_action_accounts"
|
||||
android:title="@string/accounts"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@id/filters"
|
||||
android:layout_weight="0"
|
||||
android:icon="@drawable/ic_action_speaker_muted"
|
||||
android:title="@string/filters"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@id/settings"
|
||||
android:layout_weight="0"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:title="@string/settings"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:layout_weight="1"
|
||||
android:enabled="false"
|
||||
android:title=""
|
||||
app:showAsAction="always|withText"/>
|
||||
<item
|
||||
android:layout_weight="0"
|
||||
android:enabled="false"
|
||||
android:icon="@android:color/transparent"
|
||||
android:title="@null"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
|
@ -44,6 +44,7 @@
|
|||
<string name="password">Password</string>
|
||||
<string name="label_data_provider">Twidere database provider</string>
|
||||
<string name="label_refresh_service">Refresh service</string>
|
||||
<string name="label_streaming_service">Streaming service</string>
|
||||
<string name="label_background_operation_service">Background operation service</string>
|
||||
<string name="open_in_browser">Open in browser</string>
|
||||
<string name="tap_to_load_more">Tap to load more</string>
|
||||
|
@ -754,4 +755,8 @@
|
|||
<string name="play">Play</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="jump_to_top">Jump to top</string>
|
||||
<string name="timeline_streaming_running">Timeline streaming running</string>
|
||||
<string name="streaming">Streaming</string>
|
||||
<string name="enable_streaming">Enable streaming</string>
|
||||
<string name="dark_theme">Dark theme</string>
|
||||
</resources>
|
|
@ -3,7 +3,13 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/auto_refresh">
|
||||
|
||||
<org.mariotaku.twidere.preference.AutoFixSwitchPreference
|
||||
android:disableDependentsState="false"
|
||||
android:key="auto_refresh"
|
||||
android:title="@string/auto_refresh"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:dependency="auto_refresh"
|
||||
android:key="cat_refresh_content"
|
||||
android:title="@string/content_to_refresh">
|
||||
<CheckBoxPreference
|
||||
|
@ -24,4 +30,15 @@
|
|||
android:title="@string/trends"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:enabled="false"
|
||||
android:key="cat_streaming"
|
||||
android:title="@string/streaming">
|
||||
|
||||
<org.mariotaku.twidere.preference.AutoFixSwitchPreference
|
||||
android:key="enable_streaming"
|
||||
android:title="@string/enable_streaming"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
|
@ -18,12 +18,6 @@
|
|||
android:summary="@string/silent_notifications_summary"
|
||||
android:title="@string/silent_notifications"/>
|
||||
|
||||
<org.mariotaku.twidere.preference.AutoFixCheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="background_toast_notification"
|
||||
android:summary="@string/background_toast_notification_summary"
|
||||
android:title="@string/background_toast_notification"/>
|
||||
|
||||
<org.mariotaku.twidere.preference.AutoFixCheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="pebble_notifications"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/theme">
|
||||
|
||||
|
@ -7,20 +8,18 @@
|
|||
android:key="cat_theme_preview"
|
||||
android:order="11"
|
||||
android:title="@string/preview">
|
||||
<org.mariotaku.twidere.preference.ThemePreviewPreference android:key="theme_preview" />
|
||||
<org.mariotaku.twidere.preference.ThemePreviewPreference
|
||||
android:key="theme_preview"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<org.mariotaku.twidere.preference.SummaryListPreference
|
||||
android:defaultValue="twidere"
|
||||
android:entries="@array/entries_theme"
|
||||
android:entryValues="@array/values_theme"
|
||||
android:key="theme"
|
||||
<org.mariotaku.twidere.preference.DarkLightThemeTogglePreference
|
||||
android:order="21"
|
||||
android:title="@string/style">
|
||||
android:key="theme"
|
||||
android:title="@string/dark_theme">
|
||||
<extra
|
||||
android:name="notify_change"
|
||||
android:value="true" />
|
||||
</org.mariotaku.twidere.preference.SummaryListPreference>
|
||||
android:value="true"/>
|
||||
</org.mariotaku.twidere.preference.DarkLightThemeTogglePreference>
|
||||
|
||||
<org.mariotaku.twidere.preference.ThemeBackgroundPreference
|
||||
android:defaultValue="default"
|
||||
|
@ -28,7 +27,7 @@
|
|||
android:title="@string/background">
|
||||
<extra
|
||||
android:name="notify_change"
|
||||
android:value="true" />
|
||||
android:value="true"/>
|
||||
</org.mariotaku.twidere.preference.ThemeBackgroundPreference>
|
||||
|
||||
<org.mariotaku.twidere.preference.ColorPickerPreference
|
||||
|
@ -38,7 +37,7 @@
|
|||
app:defaultColor="@color/branding_color">
|
||||
<extra
|
||||
android:name="notify_change"
|
||||
android:value="true" />
|
||||
android:value="true"/>
|
||||
</org.mariotaku.twidere.preference.ColorPickerPreference>
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="white" d="M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26l1.46-1.46c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74l-1.46 1.46c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z"/>
|
||||
<path fill="none" d="M0 0h24v24h-24z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 398 B |
|
@ -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,16 +17,24 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.extension.streaming;
|
||||
package org.mariotaku.twidere.util;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
* Created by mariotaku on 15/5/27.
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
public class DebugModeUtils {
|
||||
|
||||
public static void initForHttpClient(final OkHttpClient client) {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void initForApplication(final Application application) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
}
|