improved swipe refresh progress position
updated sdk version
This commit is contained in:
parent
aca39a6016
commit
470ecc3726
|
@ -17,8 +17,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion '22.0.1'
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.0'
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Sun Aug 16 21:56:31 CST 2015
|
||||
#Wed Aug 19 14:17:02 CST 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip
|
||||
|
|
|
@ -5,7 +5,7 @@ include ':twidere.wear'
|
|||
include ':twidere.donate.nyanwp'
|
||||
include ':twidere.donate.nyanwp.wear'
|
||||
include ':twidere.component.nyan'
|
||||
include ':twidere.extension.twitlonger'
|
||||
//include ':twidere.extension.twitlonger'
|
||||
include ':twidere.extension.push.xiaomi'
|
||||
include ':twidere.extension.launcher.compose'
|
||||
include ':twidere.extension.shortener.gist'
|
|
@ -13,7 +13,7 @@ android {
|
|||
defaultConfig {
|
||||
applicationId "org.mariotaku.twidere"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
targetSdkVersion 23
|
||||
versionCode 117
|
||||
versionName "0.3.0"
|
||||
multiDexEnabled true
|
||||
|
@ -59,10 +59,10 @@ dependencies {
|
|||
apt 'com.bluelinelabs:logansquare-compiler:1.1.0'
|
||||
apt 'com.hannesdorfmann.parcelableplease:processor:1.0.1'
|
||||
compile 'com.android.support:multidex:1.0.1'
|
||||
compile 'com.android.support:support-v13:22.2.1'
|
||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||
compile 'com.android.support:cardview-v7:22.2.1'
|
||||
compile 'com.android.support:recyclerview-v7:22.2.1'
|
||||
compile 'com.android.support:support-v13:23.0.0'
|
||||
compile 'com.android.support:appcompat-v7:23.0.0'
|
||||
compile 'com.android.support:cardview-v7:23.0.0'
|
||||
compile 'com.android.support:recyclerview-v7:23.0.0'
|
||||
compile 'com.twitter:twitter-text:1.12.1'
|
||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
|
||||
compile 'com.squareup:otto:1.3.8'
|
||||
|
@ -91,7 +91,7 @@ dependencies {
|
|||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
|
||||
compile 'com.github.mariotaku:PickNCrop:44b09cbc69'
|
||||
compile 'com.diogobernardino:williamchart:2.0.1'
|
||||
googleCompile 'com.google.android.gms:play-services-maps:7.5.0'
|
||||
googleCompile 'com.google.android.gms:play-services-maps:7.8.0'
|
||||
googleCompile 'com.google.maps.android:android-maps-utils:0.4'
|
||||
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
||||
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.osmdroid.views.MapView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/map_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -20,59 +20,69 @@
|
|||
package edu.tsinghua.hotmobi;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.app.TwidereApplication;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import edu.tsinghua.hotmobi.model.BaseEvent;
|
||||
import edu.tsinghua.hotmobi.model.FirstLaunchEvent;
|
||||
import edu.tsinghua.hotmobi.model.LatLng;
|
||||
import edu.tsinghua.hotmobi.model.MediaEvent;
|
||||
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
||||
import edu.tsinghua.hotmobi.model.SessionEvent;
|
||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||
import edu.tsinghua.hotmobi.model.TweetType;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/10.
|
||||
*/
|
||||
public class HotMobiLogger {
|
||||
|
||||
public static final long ACCOUNT_ID_NOT_NEEDED = -1;
|
||||
|
||||
private static final String LOGTAG = "HotMobiLogger";
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
public HotMobiLogger() {
|
||||
mExecutor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
public void log(BaseEvent event) {
|
||||
|
||||
mExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String getLogFilename(BaseEvent event) {
|
||||
if (event instanceof FirstLaunchEvent) {
|
||||
return "first_launch";
|
||||
} else if (event instanceof RefreshEvent) {
|
||||
if (event instanceof RefreshEvent) {
|
||||
return "refresh";
|
||||
} else if (event instanceof SessionEvent) {
|
||||
return "session";
|
||||
} else if (event instanceof TweetEvent) {
|
||||
return "tweet";
|
||||
} else if (event instanceof MediaEvent) {
|
||||
return "media";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getTweetType(ParcelableStatus status) {
|
||||
return TweetType.TEXT;
|
||||
public static String getInstallationSerialId(Context context) {
|
||||
final SharedPreferences prefs = context.getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
final String persistedDeviceId = prefs.getString(Constants.KEY_DEVICE_SERIAL, null);
|
||||
final String uuid;
|
||||
if (!TextUtils.isEmpty(persistedDeviceId)) {
|
||||
uuid = persistedDeviceId.replaceAll("[^\\w\\d]", "");
|
||||
} else {
|
||||
uuid = UUID.randomUUID().toString().replaceAll("[^\\w\\d]", "");
|
||||
prefs.edit().putString(Constants.KEY_DEVICE_SERIAL, uuid).apply();
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public static HotMobiLogger getInstance(Context context) {
|
||||
|
@ -84,4 +94,22 @@ public class HotMobiLogger {
|
|||
if (location == null) return null;
|
||||
return new LatLng(location.getLatitude(), location.getLongitude());
|
||||
}
|
||||
|
||||
public void log(long accountId, final Object event) {
|
||||
|
||||
mExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Log.d(LOGTAG, LoganSquare.serialize(event));
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void log(Object event) {
|
||||
log(ACCOUNT_ID_NOT_NEEDED, event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +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 edu.tsinghua.hotmobi.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/8.
|
||||
*/
|
||||
@JsonObject
|
||||
public class FirstLaunchEvent extends BaseEvent{
|
||||
@JsonField(name = "device_id")
|
||||
String deviceId;
|
||||
@JsonField(name = "configuration")
|
||||
Map<String, String> configuration;
|
||||
|
||||
}
|
|
@ -27,8 +27,6 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
|
|||
import org.mariotaku.twidere.model.ParcelableMedia;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/7.
|
||||
*/
|
||||
|
@ -39,12 +37,10 @@ public class MediaEvent extends BaseEvent {
|
|||
long id;
|
||||
@JsonField(name = "user_id")
|
||||
long userId;
|
||||
@JsonField(name = "tweet_type")
|
||||
int tweetType;
|
||||
@JsonField(name = "timeline_type")
|
||||
int timelineType;
|
||||
@JsonField(name = "action")
|
||||
int action;
|
||||
@JsonField(name = "tweet_type", typeConverter = TweetType.TweetTypeConverter.class)
|
||||
TweetType tweetType;
|
||||
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||
TimelineType timelineType;
|
||||
@JsonField(name = "preview_url")
|
||||
String previewUrl;
|
||||
@JsonField(name = "media_url")
|
||||
|
@ -52,7 +48,7 @@ public class MediaEvent extends BaseEvent {
|
|||
@JsonField(name = "preview_enabled")
|
||||
boolean previewEnabled;
|
||||
|
||||
public static MediaEvent create(Context context, ParcelableStatus status, ParcelableMedia media, int timelineType, boolean previewEnabled) {
|
||||
public static MediaEvent create(Context context, ParcelableStatus status, ParcelableMedia media, TimelineType timelineType, boolean previewEnabled) {
|
||||
final MediaEvent event = new MediaEvent();
|
||||
event.markStart(context);
|
||||
event.setId(status.id);
|
||||
|
@ -61,7 +57,7 @@ public class MediaEvent extends BaseEvent {
|
|||
event.setPreviewUrl(media.preview_url);
|
||||
event.setPreviewEnabled(previewEnabled);
|
||||
event.setTimelineType(timelineType);
|
||||
event.setTweetType(HotMobiLogger.getTweetType(status));
|
||||
event.setTweetType(TweetType.getTweetType(status));
|
||||
return event;
|
||||
}
|
||||
|
||||
|
@ -77,10 +73,6 @@ public class MediaEvent extends BaseEvent {
|
|||
this.previewUrl = previewUrl;
|
||||
}
|
||||
|
||||
public void setAction(int action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -89,20 +81,13 @@ public class MediaEvent extends BaseEvent {
|
|||
this.userId = userId;
|
||||
}
|
||||
|
||||
public void setTweetType(int tweetType) {
|
||||
public void setTweetType(TweetType tweetType) {
|
||||
this.tweetType = tweetType;
|
||||
}
|
||||
|
||||
public void setTimelineType(int timelineType) {
|
||||
public void setTimelineType(TimelineType timelineType) {
|
||||
this.timelineType = timelineType;
|
||||
}
|
||||
|
||||
|
||||
public interface Action {
|
||||
int OPEN = 0;
|
||||
int RETWEET = 1;
|
||||
int FAVORITE = 2;
|
||||
|
||||
int UNFAVORITE = -2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,18 +32,18 @@ public class RefreshEvent extends BaseEvent {
|
|||
@JsonField(name = "ids")
|
||||
long[] ids;
|
||||
|
||||
@JsonField(name = "timeline_type")
|
||||
int timelineType;
|
||||
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||
TimelineType timelineType;
|
||||
|
||||
public void setIds(long[] ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public void setTimelineType(int timelineType) {
|
||||
public void setTimelineType(TimelineType timelineType) {
|
||||
this.timelineType = timelineType;
|
||||
}
|
||||
|
||||
public static RefreshEvent create(final Context context, long[] ids, int timelineType) {
|
||||
public static RefreshEvent create(final Context context, long[] ids, TimelineType timelineType) {
|
||||
final RefreshEvent event = new RefreshEvent();
|
||||
event.markStart(context);
|
||||
event.setIds(ids);
|
||||
|
|
|
@ -27,8 +27,30 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
|
|||
*/
|
||||
@JsonObject
|
||||
public class ScrollRecord {
|
||||
@JsonField(name = "count")
|
||||
long count;
|
||||
@JsonField(name = "total")
|
||||
long total;
|
||||
@JsonField(name = "id")
|
||||
long id;
|
||||
@JsonField(name = "timestamp")
|
||||
long timestamp;
|
||||
@JsonField(name = "scroll_state")
|
||||
int scrollState;
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setScrollState(int scrollState) {
|
||||
this.scrollState = scrollState;
|
||||
}
|
||||
|
||||
public static ScrollRecord create(long id, long timestamp, int scrollState) {
|
||||
final ScrollRecord record = new ScrollRecord();
|
||||
record.setId(id);
|
||||
record.setTimestamp(timestamp);
|
||||
record.setScrollState(scrollState);
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,32 @@
|
|||
|
||||
package edu.tsinghua.hotmobi.model;
|
||||
|
||||
import java.util.Map;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/8.
|
||||
*/
|
||||
@JsonObject
|
||||
public class SessionEvent extends BaseEvent {
|
||||
|
||||
Map<String, ScrollRecord> scrollRecords;
|
||||
@JsonField(name = "configuration")
|
||||
String configuration;
|
||||
|
||||
|
||||
public static SessionEvent create(Context context) {
|
||||
final SessionEvent event = new SessionEvent();
|
||||
event.markStart(context);
|
||||
final Context appContext = context.getApplicationContext();
|
||||
final Configuration conf = appContext.getResources().getConfiguration();
|
||||
event.setConfiguration(conf.toString());
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setConfiguration(String configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 edu.tsinghua.hotmobi.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/18.
|
||||
*/
|
||||
public enum TimelineType {
|
||||
HOME("home"), INTERACTIONS("interactions"), OTHER("other");
|
||||
|
||||
private final String value;
|
||||
|
||||
TimelineType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static TimelineType parse(String type) {
|
||||
if (HOME.value.equalsIgnoreCase(type)) {
|
||||
return HOME;
|
||||
} else if (INTERACTIONS.value.equalsIgnoreCase(type)) {
|
||||
return INTERACTIONS;
|
||||
}
|
||||
return OTHER;
|
||||
}
|
||||
|
||||
public static class TimelineTypeConverter extends StringBasedTypeConverter<TimelineType> {
|
||||
|
||||
@Override
|
||||
public TimelineType getFromString(String string) {
|
||||
return TimelineType.parse(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToString(TimelineType timelineType) {
|
||||
if (timelineType == null) return null;
|
||||
return timelineType.value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,11 +23,10 @@ import android.content.Context;
|
|||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
|
||||
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/7.
|
||||
*/
|
||||
|
@ -36,16 +35,33 @@ public class TweetEvent extends BaseEvent {
|
|||
|
||||
@JsonField(name = "id")
|
||||
long id;
|
||||
@JsonField(name = "account_id")
|
||||
long accountId;
|
||||
@JsonField(name = "user_id")
|
||||
long userId;
|
||||
@JsonField(name = "tweet_type")
|
||||
int tweetType;
|
||||
@JsonField(name = "timeline_type")
|
||||
int timelineType;
|
||||
@JsonField(name = "action")
|
||||
int action;
|
||||
@JsonField(name = "tweet_type", typeConverter = TweetType.TweetTypeConverter.class)
|
||||
TweetType tweetType;
|
||||
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||
TimelineType timelineType;
|
||||
@JsonField(name = "action", typeConverter = Action.TweetActionConverter.class)
|
||||
Action action;
|
||||
|
||||
public void setAction(int action) {
|
||||
public static TweetEvent create(Context context, ParcelableStatus status, TimelineType timelineType) {
|
||||
final TweetEvent event = new TweetEvent();
|
||||
event.markStart(context);
|
||||
event.setId(status.id);
|
||||
event.setAccountId(status.account_id);
|
||||
event.setUserId(status.user_id);
|
||||
event.setTimelineType(timelineType);
|
||||
event.setTweetType(TweetType.getTweetType(status));
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public void setAction(Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
@ -57,30 +73,54 @@ public class TweetEvent extends BaseEvent {
|
|||
this.userId = userId;
|
||||
}
|
||||
|
||||
public void setTweetType(int tweetType) {
|
||||
public void setTweetType(TweetType tweetType) {
|
||||
this.tweetType = tweetType;
|
||||
}
|
||||
|
||||
public void setTimelineType(int timelineType) {
|
||||
public void setTimelineType(TimelineType timelineType) {
|
||||
this.timelineType = timelineType;
|
||||
}
|
||||
|
||||
public static TweetEvent create(Context context, ParcelableStatus status, int timelineType) {
|
||||
final TweetEvent event = new TweetEvent();
|
||||
event.markStart(context);
|
||||
event.setId(status.id);
|
||||
event.setUserId(status.user_id);
|
||||
event.setTimelineType(timelineType);
|
||||
event.setTweetType(HotMobiLogger.getTweetType(status));
|
||||
return event;
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
|
||||
public interface Action {
|
||||
int OPEN = 0;
|
||||
int RETWEET = 1;
|
||||
int FAVORITE = 2;
|
||||
public enum Action {
|
||||
OPEN("open"), RETWEET("retweet"), FAVORITE("favorite"), UNFAVORITE("unfavorite"), UNKNOWN("unknown");
|
||||
|
||||
int UNFAVORITE =-2;
|
||||
private final String value;
|
||||
|
||||
Action(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Action parse(String action) {
|
||||
if (OPEN.value.equalsIgnoreCase(action)) {
|
||||
return OPEN;
|
||||
} else if (RETWEET.value.equalsIgnoreCase(action)) {
|
||||
return RETWEET;
|
||||
} else if (FAVORITE.value.equalsIgnoreCase(action)) {
|
||||
return FAVORITE;
|
||||
} else if (UNFAVORITE.value.equalsIgnoreCase(action)) {
|
||||
return UNFAVORITE;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
public static class TweetActionConverter extends StringBasedTypeConverter<Action> {
|
||||
|
||||
@Override
|
||||
public Action getFromString(String string) {
|
||||
return Action.parse(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToString(Action action) {
|
||||
if (action == null) return null;
|
||||
return action.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,71 @@
|
|||
|
||||
package edu.tsinghua.hotmobi.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
|
||||
|
||||
import org.mariotaku.twidere.model.ParcelableMedia;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/8/13.
|
||||
*/
|
||||
public interface TweetType {
|
||||
int TEXT = 0;
|
||||
int PHOTO = 1;
|
||||
int VIDEO = 2;
|
||||
int OTHER = 3;
|
||||
public enum TweetType {
|
||||
TEXT("text"), PHOTO("photo"), VIDEO("video"), OTHER("other");
|
||||
|
||||
public static TweetType getTweetType(ParcelableStatus status) {
|
||||
if (status.media != null) {
|
||||
boolean hasImage = false;
|
||||
for (ParcelableMedia media : status.media) {
|
||||
switch (media.type) {
|
||||
case ParcelableMedia.TYPE_ANIMATED_GIF:
|
||||
case ParcelableMedia.TYPE_CARD_ANIMATED_GIF:
|
||||
case ParcelableMedia.TYPE_VIDEO:
|
||||
return VIDEO;
|
||||
case ParcelableMedia.TYPE_IMAGE: {
|
||||
hasImage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasImage) {
|
||||
return PHOTO;
|
||||
}
|
||||
}
|
||||
return TEXT;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private final String value;
|
||||
|
||||
TweetType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static TweetType parse(String type) {
|
||||
if (TEXT.value.equalsIgnoreCase(type)) {
|
||||
return TEXT;
|
||||
} else if (PHOTO.value.equalsIgnoreCase(type)) {
|
||||
return PHOTO;
|
||||
} else if (VIDEO.value.equalsIgnoreCase(type)) {
|
||||
return VIDEO;
|
||||
}
|
||||
return OTHER;
|
||||
}
|
||||
|
||||
public static class TweetTypeConverter extends StringBasedTypeConverter<TweetType> {
|
||||
|
||||
@Override
|
||||
public TweetType getFromString(String string) {
|
||||
return TweetType.parse(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToString(TweetType tweetType) {
|
||||
if (tweetType == null) return null;
|
||||
return tweetType.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,6 +114,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
import edu.tsinghua.hotmobi.model.SessionEvent;
|
||||
import edu.tsinghua.spice.Utilies.NetworkStateUtil;
|
||||
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
||||
|
||||
|
@ -168,6 +170,7 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
private ControlBarShowHideHelper mControlBarShowHideHelper = new ControlBarShowHideHelper(this);
|
||||
private int mTabColumns;
|
||||
private View mActionBarContainer;
|
||||
private SessionEvent mSessionEvent;
|
||||
|
||||
public void closeAccountsDrawer() {
|
||||
if (mDrawerLayout == null) return;
|
||||
|
@ -450,12 +453,14 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
final Bus bus = TwidereApplication.getInstance(this).getMessageBus();
|
||||
assert bus != null;
|
||||
bus.register(this);
|
||||
// spice
|
||||
// BEGIN HotMobi
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Launch" + "," + Build.MODEL
|
||||
+ "," + "mediaPreview=" + mPreferences.getBoolean(KEY_MEDIA_PREVIEW, false));
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Launch"
|
||||
+ "," + NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
||||
//end
|
||||
SessionEvent event = SessionEvent.create(this);
|
||||
mSessionEvent = event;
|
||||
// END HotMobi
|
||||
mReadStateManager.registerOnSharedPreferenceChangeListener(mReadStateChangeListener);
|
||||
updateUnreadCount();
|
||||
}
|
||||
|
@ -488,11 +493,15 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
|||
mPreferences.edit().putInt(KEY_SAVED_TAB_POSITION, mViewPager.getCurrentItem()).apply();
|
||||
sendBroadcast(new Intent(BROADCAST_HOME_ACTIVITY_ONSTOP));
|
||||
|
||||
// spice
|
||||
// BEGIN HotMobi
|
||||
final SessionEvent event = mSessionEvent;
|
||||
event.markEnd();
|
||||
HotMobiLogger.getInstance(this).log(event);
|
||||
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Stop");
|
||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Stop" + ","
|
||||
+ NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
||||
//end
|
||||
// END HotMobi
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@ import org.mariotaku.twidere.util.ThemeUtils;
|
|||
import org.mariotaku.twidere.util.TwidereColorUtils;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
|
||||
import org.mariotaku.twidere.view.iface.IExtendedView;
|
||||
import org.mariotaku.twidere.view.themed.AccentSwipeRefreshLayout;
|
||||
|
||||
/**
|
||||
* Comment, blah, blah, blah.
|
||||
|
@ -179,10 +181,10 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
if (activity instanceof IControlBarActivity) {
|
||||
((IControlBarActivity) activity).registerControlBarOffsetListener(this);
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof IControlBarActivity) {
|
||||
((IControlBarActivity) context).registerControlBarOffsetListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,15 +212,28 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
|||
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
|
||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
updateRefreshProgressOffset();
|
||||
if (mSwipeRefreshLayout instanceof AccentSwipeRefreshLayout) {
|
||||
((AccentSwipeRefreshLayout) mSwipeRefreshLayout).setTouchInterceptor(new IExtendedView.TouchInterceptor() {
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(View view, MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(View view, MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
updateRefreshProgressOffset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(View view, MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (compact) {
|
||||
mItemDecoration = new DividerItemDecoration(context, mLayoutManager.getOrientation());
|
||||
mRecyclerView.addItemDecoration(mItemDecoration);
|
||||
|
@ -326,17 +341,20 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
|||
|
||||
protected void updateRefreshProgressOffset() {
|
||||
final FragmentActivity activity = getActivity();
|
||||
if (!(activity instanceof IControlBarActivity) || mSystemWindowsInsets.top == 0 || mSwipeRefreshLayout == null
|
||||
final Rect insets = this.mSystemWindowsInsets;
|
||||
final SwipeRefreshLayout layout = this.mSwipeRefreshLayout;
|
||||
if (!(activity instanceof IControlBarActivity) || insets.top == 0 || layout == null
|
||||
|| isRefreshing()) {
|
||||
return;
|
||||
}
|
||||
final int progressCircleDiameter = layout.getProgressCircleDiameter();
|
||||
if (progressCircleDiameter == 0) return;
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
final int progressCircleDiameter = mSwipeRefreshLayout.getProgressCircleDiameter();
|
||||
final IControlBarActivity control = (IControlBarActivity) activity;
|
||||
final int controlBarOffsetPixels = Math.round(control.getControlBarHeight() * (1 - control.getControlBarOffset()));
|
||||
final int swipeStart = (mSystemWindowsInsets.top - controlBarOffsetPixels) - progressCircleDiameter;
|
||||
final int swipeStart = (insets.top - controlBarOffsetPixels) - progressCircleDiameter;
|
||||
// 64: SwipeRefreshLayout.DEFAULT_CIRCLE_TARGET
|
||||
final int swipeDistance = Math.round(64 * density);
|
||||
mSwipeRefreshLayout.setProgressViewOffset(false, swipeStart, swipeStart + swipeDistance);
|
||||
layout.setProgressViewOffset(false, swipeStart, swipeStart + swipeDistance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ import org.mariotaku.twidere.view.holder.StatusViewHolder;
|
|||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
import edu.tsinghua.hotmobi.model.MediaEvent;
|
||||
import edu.tsinghua.hotmobi.model.ScrollRecord;
|
||||
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||
|
||||
import static org.mariotaku.twidere.util.Utils.setMenuForStatus;
|
||||
|
||||
|
@ -85,6 +87,38 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
|||
}
|
||||
};
|
||||
|
||||
private OnScrollListener mHotMobiScrollTracker = new OnScrollListener() {
|
||||
|
||||
private long mFirstVisibleId = -1;
|
||||
private int mFirstVisiblePosition = -1;
|
||||
private int mScrollState;
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
|
||||
final int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
|
||||
if (firstVisiblePosition != mFirstVisiblePosition) {
|
||||
//noinspection unchecked
|
||||
final AbsStatusesAdapter<Data> adapter = (AbsStatusesAdapter<Data>) recyclerView.getAdapter();
|
||||
final ParcelableStatus status = adapter.getStatus(firstVisiblePosition);
|
||||
if (status != null) {
|
||||
final long id = status.id, accountId = status.account_id;
|
||||
if (id != mFirstVisibleId) {
|
||||
final ScrollRecord record = ScrollRecord.create(id, System.currentTimeMillis(), mScrollState);
|
||||
HotMobiLogger.getInstance(getActivity()).log(accountId, record);
|
||||
}
|
||||
mFirstVisibleId = id;
|
||||
}
|
||||
}
|
||||
mFirstVisiblePosition = firstVisiblePosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||
mScrollState = newState;
|
||||
}
|
||||
};
|
||||
|
||||
protected AbsStatusesFragment() {
|
||||
mStatusesBusCallback = createMessageBusCallback();
|
||||
}
|
||||
|
@ -259,8 +293,9 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
|||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||
Utils.openMedia(getActivity(), status, media, options);
|
||||
// BEGIN HotMobi
|
||||
final MediaEvent event = MediaEvent.create(getActivity(), status, media, 0, adapter.isMediaPreviewEnabled());
|
||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
||||
final MediaEvent event = MediaEvent.create(getActivity(), status, media, TimelineType.OTHER,
|
||||
adapter.isMediaPreviewEnabled());
|
||||
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||
// END HotMobi
|
||||
}
|
||||
|
||||
|
@ -345,6 +380,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
|||
super.onStart();
|
||||
final RecyclerView recyclerView = getRecyclerView();
|
||||
recyclerView.addOnScrollListener(mOnScrollListener);
|
||||
recyclerView.addOnScrollListener(mHotMobiScrollTracker);
|
||||
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
|
||||
assert bus != null;
|
||||
bus.register(mStatusesBusCallback);
|
||||
|
@ -356,6 +392,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
|||
assert bus != null;
|
||||
bus.unregister(mStatusesBusCallback);
|
||||
final RecyclerView recyclerView = getRecyclerView();
|
||||
recyclerView.removeOnScrollListener(mHotMobiScrollTracker);
|
||||
recyclerView.removeOnScrollListener(mOnScrollListener);
|
||||
super.onStop();
|
||||
}
|
||||
|
|
|
@ -124,6 +124,7 @@ import java.util.Locale;
|
|||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
import edu.tsinghua.hotmobi.model.MediaEvent;
|
||||
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||
|
||||
/**
|
||||
|
@ -294,9 +295,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||
Utils.openMedia(getActivity(), status, media, options);
|
||||
|
||||
MediaEvent event = MediaEvent.create(getActivity(), status, media, 0,
|
||||
MediaEvent event = MediaEvent.create(getActivity(), status, media, TimelineType.OTHER,
|
||||
mStatusAdapter.isMediaPreviewEnabled());
|
||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
||||
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -374,9 +375,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||
Utils.openMediaDirectly(getActivity(), accountId, status, media, status.media, options);
|
||||
// BEGIN HotMobi
|
||||
MediaEvent event = MediaEvent.create(getActivity(), status, media, 0,
|
||||
MediaEvent event = MediaEvent.create(getActivity(), status, media, TimelineType.OTHER,
|
||||
mStatusAdapter.isMediaPreviewEnabled());
|
||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
||||
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||
// END HotMobi
|
||||
}
|
||||
|
||||
|
@ -526,7 +527,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
mStatusAdapter.setReplies(null);
|
||||
loadReplies(status);
|
||||
loadConversation(status);
|
||||
final TweetEvent event = TweetEvent.create(getActivity(), status, 0);
|
||||
final TweetEvent event = TweetEvent.create(getActivity(), status, TimelineType.OTHER);
|
||||
event.setAction(TweetEvent.Action.OPEN);
|
||||
mStatusEvent = event;
|
||||
} else {
|
||||
|
@ -867,6 +868,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
status.user_screen_name, null);
|
||||
break;
|
||||
}
|
||||
case R.id.quote_original_link: {
|
||||
Utils.openStatus(adapter.getContext(), status.account_id, status.quote_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -902,6 +906,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
favoritesContainer.setOnClickListener(this);
|
||||
retweetedByView.setOnClickListener(this);
|
||||
locationView.setOnClickListener(this);
|
||||
quoteOriginalLink.setOnClickListener(this);
|
||||
|
||||
final float defaultTextSize = adapter.getTextSize();
|
||||
nameView.setTextSize(defaultTextSize * 1.25f);
|
||||
|
@ -1574,7 +1579,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
final TweetEvent event = mStatusEvent;
|
||||
if (event == null) return;
|
||||
event.markEnd();
|
||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
||||
HotMobiLogger.getInstance(getActivity()).log(event.getAccountId(), event);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
|
||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
||||
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||
|
||||
public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||
|
@ -920,9 +921,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
|
||||
// BEGIN HotMobi
|
||||
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||
event.setAction(TweetEvent.Action.FAVORITE);
|
||||
HotMobiLogger.getInstance(getContext()).log(event);
|
||||
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||
|
||||
// END HotMobi
|
||||
|
||||
|
@ -1556,9 +1557,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
final ParcelableStatus status = result.getData();
|
||||
// BEGIN HotMobi
|
||||
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||
event.setAction(TweetEvent.Action.UNFAVORITE);
|
||||
HotMobiLogger.getInstance(getContext()).log(event);
|
||||
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||
|
||||
// END HotMobi
|
||||
|
||||
|
@ -1998,6 +1999,11 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
return Statuses.CONTENT_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TimelineType getTimelineType() {
|
||||
return TimelineType.HOME;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final List<StatusListResponse> result) {
|
||||
|
@ -2056,6 +2062,11 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
return Mentions.CONTENT_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TimelineType getTimelineType() {
|
||||
return TimelineType.INTERACTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final List<StatusListResponse> result) {
|
||||
super.onPostExecute(result);
|
||||
|
@ -2202,8 +2213,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
countCur.close();
|
||||
|
||||
// BEGIN HotMobi
|
||||
final RefreshEvent event = RefreshEvent.create(mContext, statusIds, 0);
|
||||
HotMobiLogger.getInstance(mContext).log(event);
|
||||
final RefreshEvent event = RefreshEvent.create(mContext, statusIds, getTimelineType());
|
||||
HotMobiLogger.getInstance(mContext).log(accountId, event);
|
||||
// END HotMobi
|
||||
|
||||
// Insert a gap.
|
||||
|
@ -2220,6 +2231,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
|
||||
}
|
||||
|
||||
protected abstract TimelineType getTimelineType();
|
||||
|
||||
@SafeVarargs
|
||||
@Override
|
||||
protected final void onProgressUpdate(TwitterListResponse<org.mariotaku.twidere.api.twitter.model.Status>... values) {
|
||||
|
@ -2626,9 +2639,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
|||
|
||||
// BEGIN HotMobi
|
||||
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
||||
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||
event.setAction(TweetEvent.Action.RETWEET);
|
||||
HotMobiLogger.getInstance(getContext()).log(event);
|
||||
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||
|
||||
// END HotMobi
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.mariotaku.twidere.util;
|
|||
import android.content.Context;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.mariotaku.twidere.R;
|
||||
|
||||
public class StatusCodeMessageUtils {
|
||||
|
@ -53,7 +52,8 @@ public class StatusCodeMessageUtils {
|
|||
TWITTER_ERROR_CODE_MESSAGES.put(STATUS_IS_DUPLICATE, R.string.error_twitter_187);
|
||||
TWITTER_ERROR_CODE_MESSAGES.put(193, R.string.error_twitter_193);
|
||||
TWITTER_ERROR_CODE_MESSAGES.put(215, R.string.error_twitter_215);
|
||||
HTTP_STATUS_CODE_MESSAGES.put(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, R.string.error_http_407);
|
||||
|
||||
HTTP_STATUS_CODE_MESSAGES.put(407, R.string.error_http_407);
|
||||
}
|
||||
|
||||
public static boolean containsHttpStatus(final int code) {
|
||||
|
|
|
@ -114,7 +114,6 @@ import android.widget.Toast;
|
|||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
@ -3696,8 +3695,8 @@ public final class Utils implements Constants {
|
|||
try {
|
||||
final String template = "http://translate.google.com/#%s|%s|%s";
|
||||
final String sourceLang = "auto";
|
||||
final String targetLang = URLEncoder.encode(locale.getLanguage(), HTTP.UTF_8);
|
||||
final String text = URLEncoder.encode(status.text_unescaped, HTTP.UTF_8);
|
||||
final String targetLang = URLEncoder.encode(locale.getLanguage(), "UTF-8");
|
||||
final String text = URLEncoder.encode(status.text_unescaped, "UTF-8");
|
||||
final Uri uri = Uri.parse(String.format(Locale.ROOT, template, sourceLang, targetLang, text));
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
package org.mariotaku.twidere.util.net;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A collection of utilities relating to InetAddresses.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class InetAddressUtils {
|
||||
private InetAddressUtils() {
|
||||
}
|
||||
|
||||
private static final String IPV4_BASIC_PATTERN_STRING =
|
||||
"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" + // initial 3 fields, 0-255 followed by .
|
||||
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"; // final field, 0-255
|
||||
private static final Pattern IPV4_PATTERN =
|
||||
Pattern.compile("^" + IPV4_BASIC_PATTERN_STRING + "$");
|
||||
private static final Pattern IPV4_MAPPED_IPV6_PATTERN = // TODO does not allow for redundant leading zeros
|
||||
Pattern.compile("^::[fF]{4}:" + IPV4_BASIC_PATTERN_STRING + "$");
|
||||
private static final Pattern IPV6_STD_PATTERN =
|
||||
Pattern.compile(
|
||||
"^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$");
|
||||
private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
|
||||
Pattern.compile(
|
||||
"^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)" + // 0-6 hex fields
|
||||
"::" +
|
||||
"(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})?)$"); // 0-6 hex fields
|
||||
/*
|
||||
* The above pattern is not totally rigorous as it allows for more than 7 hex fields in total
|
||||
*/
|
||||
private static final char COLON_CHAR = ':';
|
||||
// Must not have more than 7 colons (i.e. 8 fields)
|
||||
private static final int MAX_COLON_COUNT = 7;
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv4 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid IPv4 address
|
||||
*/
|
||||
public static boolean isIPv4Address(final String input) {
|
||||
return IPV4_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
public static boolean isIPv4MappedIPv64Address(final String input) {
|
||||
return IPV4_MAPPED_IPV6_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid standard (non-compressed) IPv6 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid standard (non-compressed) IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6StdAddress(final String input) {
|
||||
return IPV6_STD_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid compressed IPv6 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid compressed IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6HexCompressedAddress(final String input) {
|
||||
int colonCount = 0;
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
if (input.charAt(i) == COLON_CHAR) {
|
||||
colonCount++;
|
||||
}
|
||||
}
|
||||
return colonCount <= MAX_COLON_COUNT && IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv6 address (including compressed).
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid standard or compressed IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6Address(final String input) {
|
||||
return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ import android.util.LruCache;
|
|||
|
||||
import com.squareup.okhttp.internal.Network;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.util.HostsFileParser;
|
||||
|
|
|
@ -21,16 +21,24 @@ package org.mariotaku.twidere.view.themed;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import org.mariotaku.twidere.view.iface.IExtendedView;
|
||||
import org.mariotaku.twidere.view.iface.IThemeAccentView;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/4/25.
|
||||
*/
|
||||
public class AccentSwipeRefreshLayout extends SwipeRefreshLayout implements IThemeAccentView {
|
||||
public class AccentSwipeRefreshLayout extends SwipeRefreshLayout implements IThemeAccentView, IExtendedView {
|
||||
|
||||
private TouchInterceptor mTouchInterceptor;
|
||||
private OnSizeChangedListener mOnSizeChangedListener;
|
||||
private OnFitSystemWindowsListener mOnFitSystemWindowsListener;
|
||||
|
||||
public AccentSwipeRefreshLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
@ -43,4 +51,63 @@ public class AccentSwipeRefreshLayout extends SwipeRefreshLayout implements IThe
|
|||
public void setAccentTintColor(@NonNull ColorStateList color) {
|
||||
setColorSchemeColors(color.getDefaultColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean dispatchTouchEvent(@NonNull final MotionEvent event) {
|
||||
if (mTouchInterceptor != null) {
|
||||
final boolean ret = mTouchInterceptor.dispatchTouchEvent(this, event);
|
||||
if (ret) return true;
|
||||
}
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onInterceptTouchEvent(final MotionEvent event) {
|
||||
if (mTouchInterceptor != null) {
|
||||
final boolean ret = mTouchInterceptor.onInterceptTouchEvent(this, event);
|
||||
if (ret) return true;
|
||||
}
|
||||
return super.onInterceptTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {
|
||||
mOnFitSystemWindowsListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOnSizeChangedListener(final OnSizeChangedListener listener) {
|
||||
mOnSizeChangedListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setTouchInterceptor(final TouchInterceptor listener) {
|
||||
mTouchInterceptor = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected boolean fitSystemWindows(@NonNull Rect insets) {
|
||||
if (mOnFitSystemWindowsListener != null) {
|
||||
mOnFitSystemWindowsListener.onFitSystemWindows(insets);
|
||||
}
|
||||
return super.fitSystemWindows(insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onTouchEvent(@NonNull final MotionEvent event) {
|
||||
if (mTouchInterceptor != null) {
|
||||
final boolean ret = mTouchInterceptor.onTouchEvent(this, event);
|
||||
if (ret) return true;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
if (mOnSizeChangedListener != null) {
|
||||
mOnSizeChangedListener.onSizeChanged(this, w, h, oldw, oldh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue