1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-01-31 08:54:57 +01:00
improved notification
This commit is contained in:
Mariotaku Lee 2015-03-30 23:05:41 +08:00
parent 6fce7a8e03
commit 0b490524de
13 changed files with 395 additions and 17 deletions

View File

@ -20,6 +20,7 @@
package twitter4j.conf;
import twitter4j.http.HostAddressResolverFactory;
import twitter4j.http.HttpClientFactory;
/**
* A builder that can be used to construct a twitter4j configuration with
@ -31,7 +32,7 @@ import twitter4j.http.HostAddressResolverFactory;
* @author John Sirois - john.sirois at gmail.com
*/
@SuppressWarnings("unused")
public final class StreamConfigurationBuilder {
public final class StreamConfigurationBuilder {
private StreamConfigurationBase configuration = new StreamConfigurationBase();
@ -93,6 +94,10 @@ public final class StreamConfigurationBuilder {
return this;
}
public void setHttpClientFactory(HttpClientFactory factory) {
configuration.setHttpClientFactory(factory);
}
public StreamConfigurationBuilder setHttpConnectionTimeout(final int httpConnectionTimeout) {
checkNotBuilt();
configuration.setHttpConnectionTimeout(httpConnectionTimeout);

View File

@ -27,7 +27,7 @@ android {
minSdkVersion 14
targetSdkVersion 22
versionCode 13
versionName "1.10 (0.3.0-dev)"
versionName "1.11 (0.3.0-dev)"
}
buildTypes {
release {
@ -42,6 +42,7 @@ android {
}
dependencies {
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile project(':twidere.library.extension')
compile project(':twidere.component.twitter4j.streaming')
compile fileTree(dir: 'libs', include: ['*.jar'])

View File

@ -4,12 +4,9 @@ import org.mariotaku.twidere.TwidereConstants;
public interface Constants extends TwidereConstants {
public static final String LOGTAG = "Twidere Stream Extension";
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";
public static final String TWITTER_CONSUMER_KEY = "uAFVpMhBntJutfVj6abfA";
public static final String TWITTER_CONSUMER_SECRET = "JARXkJTfxo0F8MyctYy9bUmrLISjo8vXAHsZHYuk2E";
}

View File

@ -19,6 +19,7 @@ import android.widget.Toast;
import org.mariotaku.twidere.Twidere;
import org.mariotaku.twidere.TwidereSharedPreferences;
import org.mariotaku.twidere.extension.streaming.util.OkHttpClientFactory;
import org.mariotaku.twidere.extension.streaming.util.TwidereHostAddressResolverFactory;
import org.mariotaku.twidere.extension.streaming.util.Utils;
import org.mariotaku.twidere.library.twitter4j.streaming.BuildConfig;
@ -51,8 +52,8 @@ import static android.text.TextUtils.isEmpty;
public class StreamingService extends Service implements Constants, PrivateConstants {
private static final int NOTIFITION_SERVICE_STARTED = 1;
private static final int NOTIFITION_REQUEST_PERMISSION = 2;
private static final int NOTIFICATION_SERVICE_STARTED = 1;
private static final int NOTIFICATION_REQUEST_PERMISSION = 2;
private final List<WeakReference<TwitterStream>> mTwitterInstances = new ArrayList<>();
private ContentResolver mResolver;
@ -116,7 +117,7 @@ public class StreamingService extends Service implements Constants, PrivateConst
new Thread(new ShutdownStreamTwitterRunnable(twitter)).start();
}
mTwitterInstances.clear();
mNotificationManager.cancel(NOTIFITION_SERVICE_STARTED);
mNotificationManager.cancel(NOTIFICATION_SERVICE_STARTED);
}
@SuppressWarnings("deprecation")
@ -142,9 +143,9 @@ public class StreamingService extends Service implements Constants, PrivateConst
notification.icon = R.drawable.ic_stat_twidere;
notification.tickerText = getString(R.string.streaming_service_running);
notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
mNotificationManager.notify(NOTIFITION_SERVICE_STARTED, notification);
mNotificationManager.notify(NOTIFICATION_SERVICE_STARTED, notification);
} else {
mNotificationManager.cancel(NOTIFITION_SERVICE_STARTED);
mNotificationManager.cancel(NOTIFICATION_SERVICE_STARTED);
}
} else {
final Intent intent = new Intent(this, SettingsActivity.class);
@ -157,7 +158,7 @@ public class StreamingService extends Service implements Constants, PrivateConst
notification.icon = R.drawable.ic_stat_login;
notification.tickerText = getString(R.string.request_permission);
notification.setLatestEventInfo(this, contentTitle, contentText, contentIntent);
mNotificationManager.notify(NOTIFITION_REQUEST_PERMISSION, notification);
mNotificationManager.notify(NOTIFICATION_REQUEST_PERMISSION, notification);
}
}
@ -176,6 +177,8 @@ public class StreamingService extends Service implements Constants, PrivateConst
final long account_id = account.account_id;
mAccountIds[i] = account_id;
final StreamConfigurationBuilder cb = new StreamConfigurationBuilder();
cb.setHttpClientFactory(new OkHttpClientFactory(this));
cb.setHostAddressResolverFactory(new TwidereHostAddressResolverFactory(this));
cb.setGZIPEnabled(prefs.getBoolean(KEY_GZIP_COMPRESSING, true));
cb.setIncludeEntitiesEnabled(true);
if (prefs.getBoolean(KEY_IGNORE_SSL_ERROR, false)) {

View File

@ -0,0 +1,40 @@
/*
* 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.content.Context;
import twitter4j.http.HttpClient;
import twitter4j.http.HttpClientConfiguration;
import twitter4j.http.HttpClientFactory;
/**
* Created by mariotaku on 15/1/22.
*/
public class OkHttpClientFactory implements HttpClientFactory {
public OkHttpClientFactory(Context context) {
}
@Override
public HttpClient getInstance(HttpClientConfiguration conf) {
return new OkHttpClientImpl(conf);
}
}

View File

@ -0,0 +1,276 @@
/*
* 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.net.SSLCertificateSocketFactory;
import android.net.Uri;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request.Builder;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.internal.Internal;
import com.squareup.okhttp.internal.Network;
import org.mariotaku.twidere.TwidereConstants;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.SocketFactory;
import okio.BufferedSink;
import twitter4j.TwitterException;
import twitter4j.auth.Authorization;
import twitter4j.http.HostAddressResolver;
import twitter4j.http.HttpClient;
import twitter4j.http.HttpClientConfiguration;
import twitter4j.http.HttpParameter;
import twitter4j.http.HttpRequest;
import twitter4j.http.HttpResponse;
import twitter4j.http.RequestMethod;
/**
* Created by mariotaku on 15/1/22.
*/
public class OkHttpClientImpl implements HttpClient, TwidereConstants {
public static final MediaType APPLICATION_FORM_URLENCODED = MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8");
private final HttpClientConfiguration conf;
private final OkHttpClient client;
private final HostAddressResolver resolver;
public OkHttpClientImpl(HttpClientConfiguration conf) {
this.conf = conf;
this.resolver = conf.getHostAddressResolverFactory().getInstance(conf);
this.client = createHttpClient(conf);
}
@Override
public HttpResponse request(HttpRequest req) throws TwitterException {
final Builder builder = new Builder();
for (Entry<String, String> headerEntry : req.getRequestHeaders().entrySet()) {
builder.header(headerEntry.getKey(), headerEntry.getValue());
}
final Authorization authorization = req.getAuthorization();
if (authorization != null) {
final String authHeader = authorization.getAuthorizationHeader(req);
if (authHeader != null) {
builder.header("Authorization", authHeader);
}
}
try {
setupRequestBuilder(builder, req);
final Response response = client.newCall(builder.build()).execute();
return new OkHttpResponse(conf, null, response);
} catch (IOException e) {
throw new TwitterException(e);
}
}
@Override
public void shutdown() {
}
private OkHttpClient createHttpClient(HttpClientConfiguration conf) {
final OkHttpClient client = new OkHttpClient();
final boolean ignoreSSLError = conf.isSSLErrorIgnored();
if (ignoreSSLError) {
client.setSslSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
} else {
client.setSslSocketFactory(SSLCertificateSocketFactory.getDefault(0, null));
}
client.setSocketFactory(SocketFactory.getDefault());
client.setConnectTimeout(conf.getHttpConnectionTimeout(), TimeUnit.MILLISECONDS);
if (conf.isProxyConfigured()) {
client.setProxy(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(conf.getHttpProxyHost(),
conf.getHttpProxyPort())));
}
// client.setHostnameVerifier(new HostResolvedHostnameVerifier());
Internal.instance.setNetwork(client, new Network() {
@Override
public InetAddress[] resolveInetAddresses(String host) throws UnknownHostException {
try {
return resolver.resolve(host);
} catch (IOException e) {
if (e instanceof UnknownHostException) throw (UnknownHostException) e;
throw new UnknownHostException("Unable to resolve address " + e.getMessage());
}
}
});
return client;
}
private RequestBody getRequestBody(HttpParameter[] params) throws IOException {
if (params == null) return null;
if (!HttpParameter.containsFile(params)) {
return RequestBody.create(APPLICATION_FORM_URLENCODED, HttpParameter.encodeParameters(params));
}
final MultipartBuilder builder = new MultipartBuilder();
builder.type(MultipartBuilder.FORM);
for (final HttpParameter param : params) {
if (param.isFile()) {
RequestBody requestBody;
if (param.hasFileBody()) {
requestBody = new StreamRequestBody(MediaType.parse(param.getContentType()), param.getFileBody(), true);
} else {
requestBody = RequestBody.create(MediaType.parse(param.getContentType()), param.getFile());
}
builder.addFormDataPart(param.getName(), param.getFileName(), requestBody);
} else {
builder.addFormDataPart(param.getName(), param.getValue());
}
}
return builder.build();
}
static class StreamRequestBody extends RequestBody {
private final MediaType contentType;
private final InputStream stream;
private final boolean closeAfterWrite;
StreamRequestBody(MediaType contentType, InputStream stream, boolean closeAfterWrite) {
this.contentType = contentType;
this.stream = stream;
this.closeAfterWrite = closeAfterWrite;
}
@Override
public MediaType contentType() {
return contentType;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
int len;
byte[] buf = new byte[8192];
while ((len = stream.read(buf)) != -1) {
sink.write(buf, 0, len);
}
if (closeAfterWrite) {
Utils.closeSilently(stream);
}
}
}
private void setupRequestBuilder(Builder builder, HttpRequest req) throws IOException {
final Uri.Builder uriBuilder = Uri.parse(req.getURL()).buildUpon();
final RequestMethod method = req.getMethod();
if (method != RequestMethod.POST && method != RequestMethod.PUT) {
final HttpParameter[] parameters = req.getParameters();
if (parameters != null) {
for (HttpParameter param : parameters) {
uriBuilder.appendQueryParameter(param.getName(), param.getValue());
}
}
}
final Uri uri = uriBuilder.build();
switch (req.getMethod()) {
case GET: {
builder.get();
break;
}
case POST: {
builder.post(getRequestBody(req.getParameters()));
break;
}
case DELETE: {
builder.delete();
break;
}
case HEAD: {
builder.head();
break;
}
case PUT: {
builder.put(getRequestBody(req.getParameters()));
break;
}
default: {
throw new AssertionError();
}
}
builder.url(uri.toString());
}
private static class OkHttpResponse extends HttpResponse {
private final Response response;
public OkHttpResponse(HttpClientConfiguration conf, HttpRequest request, Response response)
throws TwitterException, IOException {
super(conf);
this.response = response;
statusCode = response.code();
if ("gzip".equals(response.header("Content-Encoding"))) {
is = new GZIPInputStream(response.body().byteStream());
} else {
is = response.body().byteStream();
}
if (!response.isSuccessful()) {
throw new TwitterException(response.message(), request, this);
}
}
@Override
public void disconnect() throws IOException {
if (is != null) {
is.close();
}
}
@Override
public String getResponseHeader(String name) {
return response.header(name);
}
@Override
public Map<String, List<String>> getResponseHeaderFields() {
final Headers headers = response.headers();
final Map<String, List<String>> maps = new HashMap<>();
for (final String name : headers.names()) {
final List<String> values = new ArrayList<>(1);
for (final String value : headers.values(name)) {
values.add(value);
}
maps.put(name, values);
}
return maps;
}
}
}

View File

@ -7,11 +7,23 @@ import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.TwidereSharedPreferences;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import java.io.Closeable;
import java.io.IOException;
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 long[] getActivatedAccountIds(final Context context) {
long[] accounts = new long[0];
if (context == null) return accounts;

View File

@ -10,7 +10,7 @@ android {
applicationId "org.mariotaku.twidere"
minSdkVersion 14
targetSdkVersion 22
versionCode 100
versionCode 101
versionName "0.3.0"
multiDexEnabled true
}

View File

@ -148,7 +148,7 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
public void setRefreshing(boolean refreshing) {
if (refreshing == mSwipeRefreshLayout.isRefreshing()) return;
if (!refreshing) updateRefreshProgressOffset();
// if (!refreshing) updateRefreshProgressOffset();
mSwipeRefreshLayout.setRefreshing(refreshing);
}
@ -454,11 +454,15 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
}
private String getCurrentReadPositionTag() {
final String tag = getReadPositionTag();
final String tag = getReadPositionTagWithAccounts();
if (tag == null) return null;
return tag + "_current";
}
private String getReadPositionTagWithAccounts() {
return Utils.getReadPositionTagWithAccounts(getReadPositionTag(), getAccountIds());
}
private void setListShown(boolean shown) {
mProgressContainer.setVisibility(shown ? View.GONE : View.VISIBLE);
mSwipeRefreshLayout.setVisibility(shown ? View.VISIBLE : View.GONE);

View File

@ -330,6 +330,7 @@ public class DirectMessagesFragment extends BaseSupportFragment implements Loade
}
public void setRefreshing(boolean refreshing) {
if (refreshing == mSwipeRefreshLayout.isRefreshing()) return;
mSwipeRefreshLayout.setRefreshing(refreshing);
}

View File

@ -821,7 +821,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
getAccountIds(getContext()));
for (final AccountPreferences pref : prefs) {
if (!pref.isHomeTimelineNotificationEnabled()) continue;
showTimelineNotification(pref, mReadStateManager.getPosition(HomeTimelineFragment.KEY_READ_POSITION_TAG));
showTimelineNotification(pref, getPositionTag(HomeTimelineFragment.KEY_READ_POSITION_TAG,
pref.getAccountId()));
}
notifyUnreadCountChanged(NOTIFICATION_ID_HOME_TIMELINE);
break;
@ -831,7 +832,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
getAccountIds(getContext()));
for (final AccountPreferences pref : prefs) {
if (!pref.isMentionsNotificationEnabled()) continue;
showMentionsNotification(pref, mReadStateManager.getPosition(MentionsTimelineFragment.KEY_READ_POSITION_TAG));
showMentionsNotification(pref, getPositionTag(MentionsTimelineFragment.KEY_READ_POSITION_TAG,
pref.getAccountId()));
}
notifyUnreadCountChanged(NOTIFICATION_ID_MENTIONS_TIMELINE);
break;
@ -853,6 +855,13 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
}
}
private long getPositionTag(String tag, long accountId) {
final long position = mReadStateManager.getPosition(Utils.getReadPositionTagWithAccounts(tag,
accountId));
if (position != -1) return position;
return mReadStateManager.getPosition(tag);
}
private void showTimelineNotification(AccountPreferences pref, long position) {
final long accountId = pref.getAccountId();
final Context context = getContext();

View File

@ -63,6 +63,11 @@ public class ReadStateManager implements Constants {
return pairs;
}
public long getPosition(final String key, final long keyId) {
return getPosition(key, String.valueOf(keyId));
}
public long getPosition(final String key, final String keyId) {
if (TextUtils.isEmpty(key)) return -1;
final Set<String> set = mPreferences.getStringSet(key, null);
@ -82,6 +87,11 @@ public class ReadStateManager implements Constants {
mPreferences.unregisterOnSharedPreferenceChangeListener(listener);
}
public boolean setPosition(final String key, final long keyId, final long position, boolean acceptOlder) {
return setPosition(key, String.valueOf(keyId), position, acceptOlder);
}
public boolean setPosition(final String key, final String keyId, final long position, boolean acceptOlder) {
if (TextUtils.isEmpty(key)) return false;
Set<String> set = mPreferences.getStringSet(key, null);

View File

@ -1084,6 +1084,26 @@ public final class Utils implements Constants, TwitterConstants {
return intent;
}
public static String getReadPositionTagWithAccounts(String tag, Bundle args) {
final long[] accountIds;
if (args.containsKey(EXTRA_ACCOUNT_IDS)) {
accountIds = args.getLongArray(EXTRA_ACCOUNT_IDS);
} else if (args.containsKey(EXTRA_ACCOUNT_ID)) {
accountIds = new long[]{args.getLong(EXTRA_ACCOUNT_ID, -1)};
} else {
accountIds = null;
}
return getReadPositionTagWithAccounts(tag, accountIds);
}
public static String getReadPositionTagWithAccounts(String tag, long... accountIds) {
if (accountIds == null || accountIds.length == 0) return tag;
final long[] accountIdsClone = accountIds.clone();
Arrays.sort(accountIdsClone);
return tag + "_" + TwidereArrayUtils.toString(accountIdsClone, '_', false);
}
public static String getStatusShareText(final Context context, final ParcelableStatus status) {
final Uri link = LinkCreator.getTwitterStatusLink(status.user_screen_name, status.id);
return context.getString(R.string.status_share_text_format_with_link,