From 470ecc37269cf44f0c4dc78ceebf569eb5818214 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Thu, 20 Aug 2015 10:19:02 +0800 Subject: [PATCH] improved swipe refresh progress position updated sdk version --- global.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- settings.gradle | 2 +- twidere/build.gradle | 12 +- .../fdroid/res/layout/activity_osm_viewer.xml | 2 +- .../edu/tsinghua/hotmobi/HotMobiLogger.java | 64 +++++++--- .../hotmobi/model/FirstLaunchEvent.java | 37 ------ .../tsinghua/hotmobi/model/MediaEvent.java | 31 ++--- .../tsinghua/hotmobi/model/RefreshEvent.java | 8 +- .../tsinghua/hotmobi/model/ScrollRecord.java | 30 ++++- .../tsinghua/hotmobi/model/SessionEvent.java | 22 +++- .../tsinghua/hotmobi/model/TimelineType.java | 58 +++++++++ .../tsinghua/hotmobi/model/TweetEvent.java | 88 ++++++++++---- .../edu/tsinghua/hotmobi/model/TweetType.java | 69 ++++++++++- .../activity/support/HomeActivity.java | 17 ++- .../AbsContentRecyclerViewFragment.java | 50 +++++--- .../fragment/support/AbsStatusesFragment.java | 41 ++++++- .../fragment/support/StatusFragment.java | 17 ++- .../twidere/util/AsyncTwitterWrapper.java | 29 +++-- .../twidere/util/StatusCodeMessageUtils.java | 4 +- .../org/mariotaku/twidere/util/Utils.java | 5 +- .../twidere/util/net/InetAddressUtils.java | 111 ++++++++++++++++++ .../util/net/TwidereHostAddressResolver.java | 1 - .../view/themed/AccentSwipeRefreshLayout.java | 69 ++++++++++- 24 files changed, 603 insertions(+), 172 deletions(-) delete mode 100644 twidere/src/main/java/edu/tsinghua/hotmobi/model/FirstLaunchEvent.java create mode 100644 twidere/src/main/java/edu/tsinghua/hotmobi/model/TimelineType.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/net/InetAddressUtils.java diff --git a/global.gradle b/global.gradle index feeba163e..c0d49cddd 100644 --- a/global.gradle +++ b/global.gradle @@ -17,8 +17,8 @@ * along with this program. If not, see . */ android { - compileSdkVersion 22 - buildToolsVersion '22.0.1' + compileSdkVersion 23 + buildToolsVersion '23.0.0' lintOptions { abortOnError false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6bf3aaa53..50c99ee33 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/settings.gradle b/settings.gradle index 86486550c..b79867b3e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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' \ No newline at end of file diff --git a/twidere/build.gradle b/twidere/build.gradle index e0778da6a..681c390fa 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -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' diff --git a/twidere/src/fdroid/res/layout/activity_osm_viewer.xml b/twidere/src/fdroid/res/layout/activity_osm_viewer.xml index 4bb321054..19dc60d0f 100644 --- a/twidere/src/fdroid/res/layout/activity_osm_viewer.xml +++ b/twidere/src/fdroid/res/layout/activity_osm_viewer.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - * - * 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 . - */ - -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 configuration; - -} diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/MediaEvent.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/MediaEvent.java index b280d1330..966df47dd 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/MediaEvent.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/MediaEvent.java @@ -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; - } } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/RefreshEvent.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/RefreshEvent.java index 398a9a0b9..854b9b4c4 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/RefreshEvent.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/RefreshEvent.java @@ -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); diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/ScrollRecord.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/ScrollRecord.java index c1c522c54..b32f23f17 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/ScrollRecord.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/ScrollRecord.java @@ -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; + } } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/SessionEvent.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/SessionEvent.java index e250f2b56..83d5e5a3f 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/SessionEvent.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/SessionEvent.java @@ -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 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; + } } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/TimelineType.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TimelineType.java new file mode 100644 index 000000000..a3a6bf4d2 --- /dev/null +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TimelineType.java @@ -0,0 +1,58 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * 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 . + */ + +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 { + + @Override + public TimelineType getFromString(String string) { + return TimelineType.parse(string); + } + + @Override + public String convertToString(TimelineType timelineType) { + if (timelineType == null) return null; + return timelineType.value; + } + } +} diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetEvent.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetEvent.java index 42b2323fe..df70c2517 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetEvent.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetEvent.java @@ -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 { + + @Override + public Action getFromString(String string) { + return Action.parse(string); + } + + @Override + public String convertToString(Action action) { + if (action == null) return null; + return action.value; + } + } } } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetType.java b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetType.java index 8b64a0ed0..fd3607756 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetType.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/model/TweetType.java @@ -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 { + + @Override + public TweetType getFromString(String string) { + return TweetType.parse(string); + } + + @Override + public String convertToString(TweetType tweetType) { + if (tweetType == null) return null; + return tweetType.value; + } + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java index 7685e9a35..d50aced95 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/HomeActivity.java @@ -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(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsContentRecyclerViewFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsContentRecyclerViewFragment.java index 54cdb57a2..ac573621e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsContentRecyclerViewFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsContentRecyclerViewFragment.java @@ -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 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 adapter = (AbsStatusesAdapter) 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 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 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 extends AbsContentRecyclerViewFr assert bus != null; bus.unregister(mStatusesBusCallback); final RecyclerView recyclerView = getRecyclerView(); + recyclerView.removeOnScrollListener(mHotMobiScrollTracker); recyclerView.removeOnScrollListener(mOnScrollListener); super.onStop(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 91ef6ce4e..07f7651ce 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -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); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java index d4d493ff7..1e98316de 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java @@ -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 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 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... 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 diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/StatusCodeMessageUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/StatusCodeMessageUtils.java index 977ad10ae..6f5c2e608 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/StatusCodeMessageUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/StatusCodeMessageUtils.java @@ -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) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index ffecc97fb..d48b421cf 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -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); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/InetAddressUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/InetAddressUtils.java new file mode 100644 index 000000000..6df666fef --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/InetAddressUtils.java @@ -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 + * . + * + */ +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); + } +} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java index 2a2eb46dd..11244a91e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereHostAddressResolver.java @@ -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; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/themed/AccentSwipeRefreshLayout.java b/twidere/src/main/java/org/mariotaku/twidere/view/themed/AccentSwipeRefreshLayout.java index cf54e1cd7..e2985e938 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/themed/AccentSwipeRefreshLayout.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/themed/AccentSwipeRefreshLayout.java @@ -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); + } + } }