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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 22
|
compileSdkVersion 23
|
||||||
buildToolsVersion '22.0.1'
|
buildToolsVersion '23.0.0'
|
||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError false
|
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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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'
|
||||||
include ':twidere.donate.nyanwp.wear'
|
include ':twidere.donate.nyanwp.wear'
|
||||||
include ':twidere.component.nyan'
|
include ':twidere.component.nyan'
|
||||||
include ':twidere.extension.twitlonger'
|
//include ':twidere.extension.twitlonger'
|
||||||
include ':twidere.extension.push.xiaomi'
|
include ':twidere.extension.push.xiaomi'
|
||||||
include ':twidere.extension.launcher.compose'
|
include ':twidere.extension.launcher.compose'
|
||||||
include ':twidere.extension.shortener.gist'
|
include ':twidere.extension.shortener.gist'
|
|
@ -13,7 +13,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.mariotaku.twidere"
|
applicationId "org.mariotaku.twidere"
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 23
|
||||||
versionCode 117
|
versionCode 117
|
||||||
versionName "0.3.0"
|
versionName "0.3.0"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
@ -59,10 +59,10 @@ dependencies {
|
||||||
apt 'com.bluelinelabs:logansquare-compiler:1.1.0'
|
apt 'com.bluelinelabs:logansquare-compiler:1.1.0'
|
||||||
apt 'com.hannesdorfmann.parcelableplease:processor:1.0.1'
|
apt 'com.hannesdorfmann.parcelableplease:processor:1.0.1'
|
||||||
compile 'com.android.support:multidex:1.0.1'
|
compile 'com.android.support:multidex:1.0.1'
|
||||||
compile 'com.android.support:support-v13:22.2.1'
|
compile 'com.android.support:support-v13:23.0.0'
|
||||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
compile 'com.android.support:appcompat-v7:23.0.0'
|
||||||
compile 'com.android.support:cardview-v7:22.2.1'
|
compile 'com.android.support:cardview-v7:23.0.0'
|
||||||
compile 'com.android.support:recyclerview-v7:22.2.1'
|
compile 'com.android.support:recyclerview-v7:23.0.0'
|
||||||
compile 'com.twitter:twitter-text:1.12.1'
|
compile 'com.twitter:twitter-text:1.12.1'
|
||||||
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'
|
||||||
compile 'com.squareup:otto:1.3.8'
|
compile 'com.squareup:otto:1.3.8'
|
||||||
|
@ -91,7 +91,7 @@ dependencies {
|
||||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
|
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
|
||||||
compile 'com.github.mariotaku:PickNCrop:44b09cbc69'
|
compile 'com.github.mariotaku:PickNCrop:44b09cbc69'
|
||||||
compile 'com.diogobernardino:williamchart:2.0.1'
|
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'
|
googleCompile 'com.google.maps.android:android-maps-utils:0.4'
|
||||||
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
||||||
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="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:id="@+id/map_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
|
@ -20,59 +20,69 @@
|
||||||
package edu.tsinghua.hotmobi;
|
package edu.tsinghua.hotmobi;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
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.app.TwidereApplication;
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
|
||||||
import org.mariotaku.twidere.util.Utils;
|
import org.mariotaku.twidere.util.Utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import edu.tsinghua.hotmobi.model.BaseEvent;
|
import edu.tsinghua.hotmobi.model.BaseEvent;
|
||||||
import edu.tsinghua.hotmobi.model.FirstLaunchEvent;
|
|
||||||
import edu.tsinghua.hotmobi.model.LatLng;
|
import edu.tsinghua.hotmobi.model.LatLng;
|
||||||
|
import edu.tsinghua.hotmobi.model.MediaEvent;
|
||||||
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
||||||
import edu.tsinghua.hotmobi.model.SessionEvent;
|
import edu.tsinghua.hotmobi.model.SessionEvent;
|
||||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||||
import edu.tsinghua.hotmobi.model.TweetType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/8/10.
|
* Created by mariotaku on 15/8/10.
|
||||||
*/
|
*/
|
||||||
public class HotMobiLogger {
|
public class HotMobiLogger {
|
||||||
|
|
||||||
|
public static final long ACCOUNT_ID_NOT_NEEDED = -1;
|
||||||
|
|
||||||
|
private static final String LOGTAG = "HotMobiLogger";
|
||||||
|
|
||||||
private final Executor mExecutor;
|
private final Executor mExecutor;
|
||||||
|
|
||||||
public HotMobiLogger() {
|
public HotMobiLogger() {
|
||||||
mExecutor = Executors.newSingleThreadExecutor();
|
mExecutor = Executors.newSingleThreadExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void log(BaseEvent event) {
|
|
||||||
|
|
||||||
mExecutor.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getLogFilename(BaseEvent event) {
|
private static String getLogFilename(BaseEvent event) {
|
||||||
if (event instanceof FirstLaunchEvent) {
|
if (event instanceof RefreshEvent) {
|
||||||
return "first_launch";
|
|
||||||
} else if (event instanceof RefreshEvent) {
|
|
||||||
return "refresh";
|
return "refresh";
|
||||||
} else if (event instanceof SessionEvent) {
|
} else if (event instanceof SessionEvent) {
|
||||||
return "session";
|
return "session";
|
||||||
} else if (event instanceof TweetEvent) {
|
} else if (event instanceof TweetEvent) {
|
||||||
return "tweet";
|
return "tweet";
|
||||||
|
} else if (event instanceof MediaEvent) {
|
||||||
|
return "media";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getTweetType(ParcelableStatus status) {
|
public static String getInstallationSerialId(Context context) {
|
||||||
return TweetType.TEXT;
|
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) {
|
public static HotMobiLogger getInstance(Context context) {
|
||||||
|
@ -84,4 +94,22 @@ public class HotMobiLogger {
|
||||||
if (location == null) return null;
|
if (location == null) return null;
|
||||||
return new LatLng(location.getLatitude(), location.getLongitude());
|
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.ParcelableMedia;
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||||
|
|
||||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/8/7.
|
* Created by mariotaku on 15/8/7.
|
||||||
*/
|
*/
|
||||||
|
@ -39,12 +37,10 @@ public class MediaEvent extends BaseEvent {
|
||||||
long id;
|
long id;
|
||||||
@JsonField(name = "user_id")
|
@JsonField(name = "user_id")
|
||||||
long userId;
|
long userId;
|
||||||
@JsonField(name = "tweet_type")
|
@JsonField(name = "tweet_type", typeConverter = TweetType.TweetTypeConverter.class)
|
||||||
int tweetType;
|
TweetType tweetType;
|
||||||
@JsonField(name = "timeline_type")
|
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||||
int timelineType;
|
TimelineType timelineType;
|
||||||
@JsonField(name = "action")
|
|
||||||
int action;
|
|
||||||
@JsonField(name = "preview_url")
|
@JsonField(name = "preview_url")
|
||||||
String previewUrl;
|
String previewUrl;
|
||||||
@JsonField(name = "media_url")
|
@JsonField(name = "media_url")
|
||||||
|
@ -52,7 +48,7 @@ public class MediaEvent extends BaseEvent {
|
||||||
@JsonField(name = "preview_enabled")
|
@JsonField(name = "preview_enabled")
|
||||||
boolean previewEnabled;
|
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();
|
final MediaEvent event = new MediaEvent();
|
||||||
event.markStart(context);
|
event.markStart(context);
|
||||||
event.setId(status.id);
|
event.setId(status.id);
|
||||||
|
@ -61,7 +57,7 @@ public class MediaEvent extends BaseEvent {
|
||||||
event.setPreviewUrl(media.preview_url);
|
event.setPreviewUrl(media.preview_url);
|
||||||
event.setPreviewEnabled(previewEnabled);
|
event.setPreviewEnabled(previewEnabled);
|
||||||
event.setTimelineType(timelineType);
|
event.setTimelineType(timelineType);
|
||||||
event.setTweetType(HotMobiLogger.getTweetType(status));
|
event.setTweetType(TweetType.getTweetType(status));
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +73,6 @@ public class MediaEvent extends BaseEvent {
|
||||||
this.previewUrl = previewUrl;
|
this.previewUrl = previewUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAction(int action) {
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(long id) {
|
public void setId(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
@ -89,20 +81,13 @@ public class MediaEvent extends BaseEvent {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTweetType(int tweetType) {
|
public void setTweetType(TweetType tweetType) {
|
||||||
this.tweetType = tweetType;
|
this.tweetType = tweetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimelineType(int timelineType) {
|
public void setTimelineType(TimelineType timelineType) {
|
||||||
this.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")
|
@JsonField(name = "ids")
|
||||||
long[] ids;
|
long[] ids;
|
||||||
|
|
||||||
@JsonField(name = "timeline_type")
|
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||||
int timelineType;
|
TimelineType timelineType;
|
||||||
|
|
||||||
public void setIds(long[] ids) {
|
public void setIds(long[] ids) {
|
||||||
this.ids = ids;
|
this.ids = ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimelineType(int timelineType) {
|
public void setTimelineType(TimelineType timelineType) {
|
||||||
this.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();
|
final RefreshEvent event = new RefreshEvent();
|
||||||
event.markStart(context);
|
event.markStart(context);
|
||||||
event.setIds(ids);
|
event.setIds(ids);
|
||||||
|
|
|
@ -27,8 +27,30 @@ import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
*/
|
*/
|
||||||
@JsonObject
|
@JsonObject
|
||||||
public class ScrollRecord {
|
public class ScrollRecord {
|
||||||
@JsonField(name = "count")
|
@JsonField(name = "id")
|
||||||
long count;
|
long id;
|
||||||
@JsonField(name = "total")
|
@JsonField(name = "timestamp")
|
||||||
long total;
|
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;
|
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.
|
* Created by mariotaku on 15/8/8.
|
||||||
*/
|
*/
|
||||||
|
@JsonObject
|
||||||
public class SessionEvent extends BaseEvent {
|
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.JsonField;
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
|
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
|
||||||
|
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||||
|
|
||||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/8/7.
|
* Created by mariotaku on 15/8/7.
|
||||||
*/
|
*/
|
||||||
|
@ -36,16 +35,33 @@ public class TweetEvent extends BaseEvent {
|
||||||
|
|
||||||
@JsonField(name = "id")
|
@JsonField(name = "id")
|
||||||
long id;
|
long id;
|
||||||
|
@JsonField(name = "account_id")
|
||||||
|
long accountId;
|
||||||
@JsonField(name = "user_id")
|
@JsonField(name = "user_id")
|
||||||
long userId;
|
long userId;
|
||||||
@JsonField(name = "tweet_type")
|
@JsonField(name = "tweet_type", typeConverter = TweetType.TweetTypeConverter.class)
|
||||||
int tweetType;
|
TweetType tweetType;
|
||||||
@JsonField(name = "timeline_type")
|
@JsonField(name = "timeline_type", typeConverter = TimelineType.TimelineTypeConverter.class)
|
||||||
int timelineType;
|
TimelineType timelineType;
|
||||||
@JsonField(name = "action")
|
@JsonField(name = "action", typeConverter = Action.TweetActionConverter.class)
|
||||||
int action;
|
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;
|
this.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,30 +73,54 @@ public class TweetEvent extends BaseEvent {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTweetType(int tweetType) {
|
public void setTweetType(TweetType tweetType) {
|
||||||
this.tweetType = tweetType;
|
this.tweetType = tweetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimelineType(int timelineType) {
|
public void setTimelineType(TimelineType timelineType) {
|
||||||
this.timelineType = timelineType;
|
this.timelineType = timelineType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TweetEvent create(Context context, ParcelableStatus status, int timelineType) {
|
public long getAccountId() {
|
||||||
final TweetEvent event = new TweetEvent();
|
return accountId;
|
||||||
event.markStart(context);
|
|
||||||
event.setId(status.id);
|
|
||||||
event.setUserId(status.user_id);
|
|
||||||
event.setTimelineType(timelineType);
|
|
||||||
event.setTweetType(HotMobiLogger.getTweetType(status));
|
|
||||||
return event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public interface Action {
|
public enum Action {
|
||||||
int OPEN = 0;
|
OPEN("open"), RETWEET("retweet"), FAVORITE("favorite"), UNFAVORITE("unfavorite"), UNKNOWN("unknown");
|
||||||
int RETWEET = 1;
|
|
||||||
int FAVORITE = 2;
|
|
||||||
|
|
||||||
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;
|
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.
|
* Created by mariotaku on 15/8/13.
|
||||||
*/
|
*/
|
||||||
public interface TweetType {
|
public enum TweetType {
|
||||||
int TEXT = 0;
|
TEXT("text"), PHOTO("photo"), VIDEO("video"), OTHER("other");
|
||||||
int PHOTO = 1;
|
|
||||||
int VIDEO = 2;
|
public static TweetType getTweetType(ParcelableStatus status) {
|
||||||
int OTHER = 3;
|
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;
|
||||||
import java.util.Map.Entry;
|
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.NetworkStateUtil;
|
||||||
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
|
||||||
|
|
||||||
|
@ -168,6 +170,7 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
||||||
private ControlBarShowHideHelper mControlBarShowHideHelper = new ControlBarShowHideHelper(this);
|
private ControlBarShowHideHelper mControlBarShowHideHelper = new ControlBarShowHideHelper(this);
|
||||||
private int mTabColumns;
|
private int mTabColumns;
|
||||||
private View mActionBarContainer;
|
private View mActionBarContainer;
|
||||||
|
private SessionEvent mSessionEvent;
|
||||||
|
|
||||||
public void closeAccountsDrawer() {
|
public void closeAccountsDrawer() {
|
||||||
if (mDrawerLayout == null) return;
|
if (mDrawerLayout == null) return;
|
||||||
|
@ -450,12 +453,14 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
||||||
final Bus bus = TwidereApplication.getInstance(this).getMessageBus();
|
final Bus bus = TwidereApplication.getInstance(this).getMessageBus();
|
||||||
assert bus != null;
|
assert bus != null;
|
||||||
bus.register(this);
|
bus.register(this);
|
||||||
// spice
|
// BEGIN HotMobi
|
||||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Launch" + "," + Build.MODEL
|
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_APP, "App Launch" + "," + Build.MODEL
|
||||||
+ "," + "mediaPreview=" + mPreferences.getBoolean(KEY_MEDIA_PREVIEW, false));
|
+ "," + "mediaPreview=" + mPreferences.getBoolean(KEY_MEDIA_PREVIEW, false));
|
||||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Launch"
|
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Launch"
|
||||||
+ "," + NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
+ "," + NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
||||||
//end
|
SessionEvent event = SessionEvent.create(this);
|
||||||
|
mSessionEvent = event;
|
||||||
|
// END HotMobi
|
||||||
mReadStateManager.registerOnSharedPreferenceChangeListener(mReadStateChangeListener);
|
mReadStateManager.registerOnSharedPreferenceChangeListener(mReadStateChangeListener);
|
||||||
updateUnreadCount();
|
updateUnreadCount();
|
||||||
}
|
}
|
||||||
|
@ -488,11 +493,15 @@ public class HomeActivity extends BaseAppCompatActivity implements OnClickListen
|
||||||
mPreferences.edit().putInt(KEY_SAVED_TAB_POSITION, mViewPager.getCurrentItem()).apply();
|
mPreferences.edit().putInt(KEY_SAVED_TAB_POSITION, mViewPager.getCurrentItem()).apply();
|
||||||
sendBroadcast(new Intent(BROADCAST_HOME_ACTIVITY_ONSTOP));
|
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_APP, "App Stop");
|
||||||
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Stop" + ","
|
SpiceProfilingUtil.profile(this, SpiceProfilingUtil.FILE_NAME_ONLAUNCH, "App Stop" + ","
|
||||||
+ NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
+ NetworkStateUtil.getConnectedType(this) + "," + Build.MODEL);
|
||||||
//end
|
// END HotMobi
|
||||||
super.onStop();
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ import org.mariotaku.twidere.util.ThemeUtils;
|
||||||
import org.mariotaku.twidere.util.TwidereColorUtils;
|
import org.mariotaku.twidere.util.TwidereColorUtils;
|
||||||
import org.mariotaku.twidere.util.Utils;
|
import org.mariotaku.twidere.util.Utils;
|
||||||
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
|
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.
|
* Comment, blah, blah, blah.
|
||||||
|
@ -179,10 +181,10 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
if (activity instanceof IControlBarActivity) {
|
if (context instanceof IControlBarActivity) {
|
||||||
((IControlBarActivity) activity).registerControlBarOffsetListener(this);
|
((IControlBarActivity) context).registerControlBarOffsetListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,15 +212,28 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
||||||
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
|
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
|
||||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||||
mRecyclerView.setHasFixedSize(true);
|
mRecyclerView.setHasFixedSize(true);
|
||||||
mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
|
if (mSwipeRefreshLayout instanceof AccentSwipeRefreshLayout) {
|
||||||
@Override
|
((AccentSwipeRefreshLayout) mSwipeRefreshLayout).setTouchInterceptor(new IExtendedView.TouchInterceptor() {
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
@Override
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
public boolean dispatchTouchEvent(View view, MotionEvent event) {
|
||||||
updateRefreshProgressOffset();
|
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) {
|
if (compact) {
|
||||||
mItemDecoration = new DividerItemDecoration(context, mLayoutManager.getOrientation());
|
mItemDecoration = new DividerItemDecoration(context, mLayoutManager.getOrientation());
|
||||||
mRecyclerView.addItemDecoration(mItemDecoration);
|
mRecyclerView.addItemDecoration(mItemDecoration);
|
||||||
|
@ -326,17 +341,20 @@ public abstract class AbsContentRecyclerViewFragment<A extends LoadMoreSupportAd
|
||||||
|
|
||||||
protected void updateRefreshProgressOffset() {
|
protected void updateRefreshProgressOffset() {
|
||||||
final FragmentActivity activity = getActivity();
|
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()) {
|
|| isRefreshing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final int progressCircleDiameter = layout.getProgressCircleDiameter();
|
||||||
|
if (progressCircleDiameter == 0) return;
|
||||||
final float density = getResources().getDisplayMetrics().density;
|
final float density = getResources().getDisplayMetrics().density;
|
||||||
final int progressCircleDiameter = mSwipeRefreshLayout.getProgressCircleDiameter();
|
|
||||||
final IControlBarActivity control = (IControlBarActivity) activity;
|
final IControlBarActivity control = (IControlBarActivity) activity;
|
||||||
final int controlBarOffsetPixels = Math.round(control.getControlBarHeight() * (1 - control.getControlBarOffset()));
|
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
|
// 64: SwipeRefreshLayout.DEFAULT_CIRCLE_TARGET
|
||||||
final int swipeDistance = Math.round(64 * density);
|
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.HotMobiLogger;
|
||||||
import edu.tsinghua.hotmobi.model.MediaEvent;
|
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;
|
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() {
|
protected AbsStatusesFragment() {
|
||||||
mStatusesBusCallback = createMessageBusCallback();
|
mStatusesBusCallback = createMessageBusCallback();
|
||||||
}
|
}
|
||||||
|
@ -259,8 +293,9 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
||||||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||||
Utils.openMedia(getActivity(), status, media, options);
|
Utils.openMedia(getActivity(), status, media, options);
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
final MediaEvent event = MediaEvent.create(getActivity(), status, media, 0, adapter.isMediaPreviewEnabled());
|
final MediaEvent event = MediaEvent.create(getActivity(), status, media, TimelineType.OTHER,
|
||||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
adapter.isMediaPreviewEnabled());
|
||||||
|
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,6 +380,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
||||||
super.onStart();
|
super.onStart();
|
||||||
final RecyclerView recyclerView = getRecyclerView();
|
final RecyclerView recyclerView = getRecyclerView();
|
||||||
recyclerView.addOnScrollListener(mOnScrollListener);
|
recyclerView.addOnScrollListener(mOnScrollListener);
|
||||||
|
recyclerView.addOnScrollListener(mHotMobiScrollTracker);
|
||||||
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
|
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
|
||||||
assert bus != null;
|
assert bus != null;
|
||||||
bus.register(mStatusesBusCallback);
|
bus.register(mStatusesBusCallback);
|
||||||
|
@ -356,6 +392,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentRecyclerViewFr
|
||||||
assert bus != null;
|
assert bus != null;
|
||||||
bus.unregister(mStatusesBusCallback);
|
bus.unregister(mStatusesBusCallback);
|
||||||
final RecyclerView recyclerView = getRecyclerView();
|
final RecyclerView recyclerView = getRecyclerView();
|
||||||
|
recyclerView.removeOnScrollListener(mHotMobiScrollTracker);
|
||||||
recyclerView.removeOnScrollListener(mOnScrollListener);
|
recyclerView.removeOnScrollListener(mOnScrollListener);
|
||||||
super.onStop();
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ import java.util.Locale;
|
||||||
|
|
||||||
import edu.tsinghua.hotmobi.HotMobiLogger;
|
import edu.tsinghua.hotmobi.HotMobiLogger;
|
||||||
import edu.tsinghua.hotmobi.model.MediaEvent;
|
import edu.tsinghua.hotmobi.model.MediaEvent;
|
||||||
|
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,9 +295,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||||
Utils.openMedia(getActivity(), status, media, options);
|
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());
|
mStatusAdapter.isMediaPreviewEnabled());
|
||||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -374,9 +375,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
final Bundle options = Utils.createMediaViewerActivityOption(view);
|
||||||
Utils.openMediaDirectly(getActivity(), accountId, status, media, status.media, options);
|
Utils.openMediaDirectly(getActivity(), accountId, status, media, status.media, options);
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
MediaEvent event = MediaEvent.create(getActivity(), status, media, 0,
|
MediaEvent event = MediaEvent.create(getActivity(), status, media, TimelineType.OTHER,
|
||||||
mStatusAdapter.isMediaPreviewEnabled());
|
mStatusAdapter.isMediaPreviewEnabled());
|
||||||
HotMobiLogger.getInstance(getActivity()).log(event);
|
HotMobiLogger.getInstance(getActivity()).log(status.account_id, event);
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,7 +527,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
mStatusAdapter.setReplies(null);
|
mStatusAdapter.setReplies(null);
|
||||||
loadReplies(status);
|
loadReplies(status);
|
||||||
loadConversation(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);
|
event.setAction(TweetEvent.Action.OPEN);
|
||||||
mStatusEvent = event;
|
mStatusEvent = event;
|
||||||
} else {
|
} else {
|
||||||
|
@ -867,6 +868,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
status.user_screen_name, null);
|
status.user_screen_name, null);
|
||||||
break;
|
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);
|
favoritesContainer.setOnClickListener(this);
|
||||||
retweetedByView.setOnClickListener(this);
|
retweetedByView.setOnClickListener(this);
|
||||||
locationView.setOnClickListener(this);
|
locationView.setOnClickListener(this);
|
||||||
|
quoteOriginalLink.setOnClickListener(this);
|
||||||
|
|
||||||
final float defaultTextSize = adapter.getTextSize();
|
final float defaultTextSize = adapter.getTextSize();
|
||||||
nameView.setTextSize(defaultTextSize * 1.25f);
|
nameView.setTextSize(defaultTextSize * 1.25f);
|
||||||
|
@ -1574,7 +1579,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
final TweetEvent event = mStatusEvent;
|
final TweetEvent event = mStatusEvent;
|
||||||
if (event == null) return;
|
if (event == null) return;
|
||||||
event.markEnd();
|
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.HotMobiLogger;
|
||||||
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
import edu.tsinghua.hotmobi.model.RefreshEvent;
|
||||||
|
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||||
import edu.tsinghua.hotmobi.model.TweetEvent;
|
import edu.tsinghua.hotmobi.model.TweetEvent;
|
||||||
|
|
||||||
public class AsyncTwitterWrapper extends TwitterWrapper {
|
public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
|
@ -920,9 +921,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
|
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
|
|
||||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||||
event.setAction(TweetEvent.Action.FAVORITE);
|
event.setAction(TweetEvent.Action.FAVORITE);
|
||||||
HotMobiLogger.getInstance(getContext()).log(event);
|
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||||
|
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
|
|
||||||
|
@ -1556,9 +1557,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
final ParcelableStatus status = result.getData();
|
final ParcelableStatus status = result.getData();
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
|
|
||||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||||
event.setAction(TweetEvent.Action.UNFAVORITE);
|
event.setAction(TweetEvent.Action.UNFAVORITE);
|
||||||
HotMobiLogger.getInstance(getContext()).log(event);
|
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||||
|
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
|
|
||||||
|
@ -1998,6 +1999,11 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
return Statuses.CONTENT_URI;
|
return Statuses.CONTENT_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineType getTimelineType() {
|
||||||
|
return TimelineType.HOME;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(final List<StatusListResponse> result) {
|
protected void onPostExecute(final List<StatusListResponse> result) {
|
||||||
|
@ -2056,6 +2062,11 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
return Mentions.CONTENT_URI;
|
return Mentions.CONTENT_URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineType getTimelineType() {
|
||||||
|
return TimelineType.INTERACTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(final List<StatusListResponse> result) {
|
protected void onPostExecute(final List<StatusListResponse> result) {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
|
@ -2202,8 +2213,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
countCur.close();
|
countCur.close();
|
||||||
|
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
final RefreshEvent event = RefreshEvent.create(mContext, statusIds, 0);
|
final RefreshEvent event = RefreshEvent.create(mContext, statusIds, getTimelineType());
|
||||||
HotMobiLogger.getInstance(mContext).log(event);
|
HotMobiLogger.getInstance(mContext).log(accountId, event);
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
|
|
||||||
// Insert a gap.
|
// Insert a gap.
|
||||||
|
@ -2220,6 +2231,8 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract TimelineType getTimelineType();
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
@Override
|
@Override
|
||||||
protected final void onProgressUpdate(TwitterListResponse<org.mariotaku.twidere.api.twitter.model.Status>... values) {
|
protected final void onProgressUpdate(TwitterListResponse<org.mariotaku.twidere.api.twitter.model.Status>... values) {
|
||||||
|
@ -2626,9 +2639,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
|
|
||||||
// BEGIN HotMobi
|
// BEGIN HotMobi
|
||||||
|
|
||||||
final TweetEvent event = TweetEvent.create(getContext(), status, 0);
|
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
|
||||||
event.setAction(TweetEvent.Action.RETWEET);
|
event.setAction(TweetEvent.Action.RETWEET);
|
||||||
HotMobiLogger.getInstance(getContext()).log(event);
|
HotMobiLogger.getInstance(getContext()).log(account_id, event);
|
||||||
|
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ package org.mariotaku.twidere.util;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
|
||||||
import org.mariotaku.twidere.R;
|
import org.mariotaku.twidere.R;
|
||||||
|
|
||||||
public class StatusCodeMessageUtils {
|
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(STATUS_IS_DUPLICATE, R.string.error_twitter_187);
|
||||||
TWITTER_ERROR_CODE_MESSAGES.put(193, R.string.error_twitter_193);
|
TWITTER_ERROR_CODE_MESSAGES.put(193, R.string.error_twitter_193);
|
||||||
TWITTER_ERROR_CODE_MESSAGES.put(215, R.string.error_twitter_215);
|
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) {
|
public static boolean containsHttpStatus(final int code) {
|
||||||
|
|
|
@ -114,7 +114,6 @@ import android.widget.Toast;
|
||||||
import com.bluelinelabs.logansquare.LoganSquare;
|
import com.bluelinelabs.logansquare.LoganSquare;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.http.protocol.HTTP;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
@ -3696,8 +3695,8 @@ public final class Utils implements Constants {
|
||||||
try {
|
try {
|
||||||
final String template = "http://translate.google.com/#%s|%s|%s";
|
final String template = "http://translate.google.com/#%s|%s|%s";
|
||||||
final String sourceLang = "auto";
|
final String sourceLang = "auto";
|
||||||
final String targetLang = URLEncoder.encode(locale.getLanguage(), HTTP.UTF_8);
|
final String targetLang = URLEncoder.encode(locale.getLanguage(), "UTF-8");
|
||||||
final String text = URLEncoder.encode(status.text_unescaped, HTTP.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 Uri uri = Uri.parse(String.format(Locale.ROOT, template, sourceLang, targetLang, text));
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
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 com.squareup.okhttp.internal.Network;
|
||||||
|
|
||||||
import org.apache.http.conn.util.InetAddressUtils;
|
|
||||||
import org.mariotaku.twidere.BuildConfig;
|
import org.mariotaku.twidere.BuildConfig;
|
||||||
import org.mariotaku.twidere.Constants;
|
import org.mariotaku.twidere.Constants;
|
||||||
import org.mariotaku.twidere.util.HostsFileParser;
|
import org.mariotaku.twidere.util.HostsFileParser;
|
||||||
|
|
|
@ -21,16 +21,24 @@ package org.mariotaku.twidere.view.themed;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import org.mariotaku.twidere.view.iface.IExtendedView;
|
||||||
import org.mariotaku.twidere.view.iface.IThemeAccentView;
|
import org.mariotaku.twidere.view.iface.IThemeAccentView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/4/25.
|
* 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) {
|
public AccentSwipeRefreshLayout(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
}
|
}
|
||||||
|
@ -43,4 +51,63 @@ public class AccentSwipeRefreshLayout extends SwipeRefreshLayout implements IThe
|
||||||
public void setAccentTintColor(@NonNull ColorStateList color) {
|
public void setAccentTintColor(@NonNull ColorStateList color) {
|
||||||
setColorSchemeColors(color.getDefaultColor());
|
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