parent
56f77bd947
commit
1e956016f5
|
@ -4,6 +4,8 @@ import com.bluelinelabs.logansquare.annotation.JsonField;
|
|||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* GNUSocial attachment model
|
||||
*
|
||||
* Created by mariotaku on 16/1/26.
|
||||
*/
|
||||
@JsonObject
|
||||
|
|
|
@ -108,6 +108,7 @@ public class TwitterException extends Exception implements TwitterResponse, Http
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@AccessLevel
|
||||
@Override
|
||||
public int getAccessLevel() {
|
||||
return InternalParseUtil.toAccessLevel(httpResponse);
|
||||
|
|
|
@ -42,6 +42,7 @@ public class PageableResponseList<T> extends ArrayList<T> implements TwitterResp
|
|||
}
|
||||
|
||||
@Override
|
||||
@AccessLevel
|
||||
public final int getAccessLevel() {
|
||||
return accessLevel;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public class QueryResult extends AbstractList<Status> implements TwitterResponse
|
|||
@JsonField(name = "statuses")
|
||||
ArrayList<Status> statuses;
|
||||
|
||||
@AccessLevel
|
||||
private int accessLevel;
|
||||
private RateLimitStatus rateLimitStatus;
|
||||
|
||||
|
@ -54,6 +55,7 @@ public class QueryResult extends AbstractList<Status> implements TwitterResponse
|
|||
accessLevel = InternalParseUtil.toAccessLevel(resp);
|
||||
}
|
||||
|
||||
@AccessLevel
|
||||
@Override
|
||||
public final int getAccessLevel() {
|
||||
return accessLevel;
|
||||
|
|
|
@ -79,6 +79,7 @@ public class ResponseList<T> extends AbstractList<T> implements TwitterResponse
|
|||
accessLevel = InternalParseUtil.toAccessLevel(resp);
|
||||
}
|
||||
|
||||
@AccessLevel
|
||||
@Override
|
||||
public final int getAccessLevel() {
|
||||
return accessLevel;
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/5/26.
|
||||
*/
|
||||
@JsonObject
|
||||
public class StatusDeletionNotice {
|
||||
public long getStatusId() {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import org.mariotaku.restfu.http.HttpResponse;
|
||||
|
||||
/**
|
||||
|
@ -32,16 +34,19 @@ import org.mariotaku.restfu.http.HttpResponse;
|
|||
*/
|
||||
public interface TwitterResponse {
|
||||
int NONE = 0;
|
||||
|
||||
int READ = 1;
|
||||
|
||||
int READ_WRITE = 2;
|
||||
int READ_WRITE_DIRECTMESSAGES = 3;
|
||||
|
||||
void processResponseHeader(HttpResponse resp);
|
||||
|
||||
@AccessLevel
|
||||
int getAccessLevel();
|
||||
|
||||
RateLimitStatus getRateLimitStatus();
|
||||
|
||||
@IntDef({NONE, READ, READ_WRITE, READ_WRITE_DIRECTMESSAGES})
|
||||
@interface AccessLevel {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@
|
|||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import org.mariotaku.restfu.http.HttpResponse;
|
||||
|
||||
import org.mariotaku.twidere.api.twitter.model.RateLimitStatus;
|
||||
import org.mariotaku.twidere.api.twitter.model.TwitterResponse;
|
||||
import org.mariotaku.twidere.api.twitter.util.InternalParseUtil;
|
||||
|
||||
/**
|
||||
|
@ -30,6 +27,7 @@ import org.mariotaku.twidere.api.twitter.util.InternalParseUtil;
|
|||
*/
|
||||
public class TwitterResponseObject implements TwitterResponse {
|
||||
|
||||
@AccessLevel
|
||||
private int accessLevel;
|
||||
private RateLimitStatus rateLimitStatus;
|
||||
|
||||
|
@ -39,6 +37,7 @@ public class TwitterResponseObject implements TwitterResponse {
|
|||
accessLevel = InternalParseUtil.toAccessLevel(resp);
|
||||
}
|
||||
|
||||
@AccessLevel
|
||||
@Override
|
||||
public final int getAccessLevel() {
|
||||
return accessLevel;
|
||||
|
|
|
@ -34,8 +34,9 @@ public final class InternalParseUtil {
|
|||
throw new AssertionError("This class should never be instantiated");
|
||||
}
|
||||
|
||||
@TwitterResponse.AccessLevel
|
||||
public static int toAccessLevel(final HttpResponse res) {
|
||||
if (null == res) return -1;
|
||||
if (res == null) return TwitterResponse.NONE;
|
||||
final String xAccessLevel = res.getHeader("X-Access-Level");
|
||||
int accessLevel;
|
||||
if (null == xAccessLevel) {
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.mariotaku.twidere.model.util.LoganSquareCursorFieldConverter;
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
@ParcelablePlease(allFields = false)
|
||||
@JsonObject
|
||||
|
|
|
@ -14,8 +14,6 @@ import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
|||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
import org.mariotaku.twidere.api.twitter.model.MediaEntity;
|
||||
import org.mariotaku.twidere.api.twitter.model.MediaEntity.Size;
|
||||
import org.mariotaku.twidere.util.TwitterContentUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
|
|
@ -19,6 +19,7 @@ public class InstagramProvider implements Provider {
|
|||
final String authority = PreviewMediaExtractor.getAuthority(link);
|
||||
if (authority == null) return false;
|
||||
switch (authority) {
|
||||
//noinspection SpellCheckingInspection
|
||||
case "instagr.am":
|
||||
case "instagram.com":
|
||||
case "www.instagram.com": {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.nyan;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
|
@ -28,6 +29,7 @@ import android.service.dreams.DreamService;
|
|||
import android.view.View;
|
||||
import android.view.View.OnSystemUiVisibilityChangeListener;
|
||||
|
||||
@SuppressLint("Registered")
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public class NyanDaydreamService extends DreamService implements NyanConstants,
|
||||
OnSharedPreferenceChangeListener, OnSystemUiVisibilityChangeListener {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.nyan;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -30,6 +31,7 @@ import android.os.PowerManager;
|
|||
import android.service.wallpaper.WallpaperService;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
@SuppressLint("Registered")
|
||||
public class NyanWallpaperService extends WallpaperService implements NyanConstants {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,16 +18,19 @@
|
|||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest package="org.mariotaku.twidere.donate.nyanwp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest
|
||||
package="org.mariotaku.twidere.donate.nyanwp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.DeviceDefault">
|
||||
android:theme="@android:style/Theme.DeviceDefault"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity
|
||||
android:name=".NyanActivity"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.mariotaku.twidere.donate.nyanwp">
|
||||
<manifest
|
||||
package="org.mariotaku.twidere.donate.nyanwp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<service
|
||||
android:name="org.mariotaku.twidere.nyan.NyanWallpaperService"
|
||||
android:exported="true"
|
||||
|
@ -24,7 +27,8 @@
|
|||
android:name="org.mariotaku.twidere.nyan.NyanDaydreamService"
|
||||
android:exported="true"
|
||||
android:label="@string/daydream_name"
|
||||
android:process=":daydream">
|
||||
android:process=":daydream"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.service.dreams.DreamService"/>
|
||||
</intent-filter>
|
||||
|
|
|
@ -20,15 +20,18 @@
|
|||
|
||||
<manifest
|
||||
package="org.mariotaku.twidere.extension.shortener.gist"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.DeviceDefault">
|
||||
android:theme="@android:style/Theme.DeviceDefault"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<meta-data
|
||||
android:name="org.mariotaku.twidere.extension"
|
||||
android:value="true"/>
|
||||
|
|
|
@ -1,567 +0,0 @@
|
|||
package com.twitter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* A class to extract usernames, lists, hashtags and URLs from Tweet text.
|
||||
*/
|
||||
public class Extractor {
|
||||
protected boolean extractURLWithoutProtocol = true;
|
||||
|
||||
/**
|
||||
* Fullwidth at sign: '@'
|
||||
*/
|
||||
private static final char FULLWIDTH_AT_SIGN = '\uff20';
|
||||
|
||||
/**
|
||||
* Fullwidth number sign: '#'
|
||||
*/
|
||||
private static final char FULLWIDTH_NUMBER_SIGN = '\uff03';
|
||||
|
||||
/**
|
||||
* Create a new extractor.
|
||||
*/
|
||||
public Extractor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract $cashtag references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract cashtags
|
||||
* @return List of cashtags referenced (without the leading $ sign)
|
||||
*/
|
||||
public List<String> extractCashtags(final String text) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
final ArrayList<String> extracted = new ArrayList<String>();
|
||||
for (final Entity entity : extractCashtagsWithIndices(text)) {
|
||||
extracted.add(entity.value);
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract $cashtag references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract cashtags
|
||||
* @return List of cashtags referenced (without the leading $ sign)
|
||||
*/
|
||||
public List<Entity> extractCashtagsWithIndices(final String text) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
// Performance optimization.
|
||||
// If text doesn't contain $, text doesn't contain
|
||||
// cashtag, so we can simply return an empty list.
|
||||
if (text.indexOf('$') == -1) return Collections.emptyList();
|
||||
|
||||
final ArrayList<Entity> extracted = new ArrayList<Entity>();
|
||||
final Matcher matcher = Regex.VALID_CASHTAG.matcher(text);
|
||||
|
||||
while (matcher.find()) {
|
||||
extracted.add(new Entity(matcher, Entity.Type.CASHTAG, Regex.VALID_CASHTAG_GROUP_CASHTAG_FULL));
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract URLs, @mentions, lists and #hashtag from a given text/tweet.
|
||||
*
|
||||
* @param text text of tweet
|
||||
* @return list of extracted entities
|
||||
*/
|
||||
public List<Entity> extractEntitiesWithIndices(final String text) {
|
||||
final ArrayList<Entity> entities = new ArrayList<Entity>();
|
||||
entities.addAll(extractURLsWithIndices(text));
|
||||
entities.addAll(extractHashtagsWithIndices(text, false));
|
||||
entities.addAll(extractMentionsOrListsWithIndices(text));
|
||||
entities.addAll(extractCashtagsWithIndices(text));
|
||||
|
||||
removeOverlappingEntities(entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract #hashtag references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract hashtags
|
||||
* @return List of hashtags referenced (without the leading # sign)
|
||||
*/
|
||||
public List<String> extractHashtags(final String text) {
|
||||
return extractHashtags(text, true);
|
||||
}
|
||||
|
||||
public List<String> extractHashtags(final String text, final boolean exclude_duplicate) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
final ArrayList<String> extracted = new ArrayList<String>();
|
||||
for (final Entity entity : extractHashtagsWithIndices(text)) {
|
||||
if (!exclude_duplicate || !extracted.contains(entity.value)) {
|
||||
extracted.add(entity.value);
|
||||
}
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract #hashtag references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract hashtags
|
||||
* @return List of hashtags referenced (without the leading # sign)
|
||||
*/
|
||||
public List<Entity> extractHashtagsWithIndices(final String text) {
|
||||
return extractHashtagsWithIndices(text, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract @username references from Tweet text. A mention is an occurance
|
||||
* of @username anywhere in a Tweet.
|
||||
*
|
||||
* @param text of the tweet from which to extract usernames
|
||||
* @return List of usernames referenced (without the leading @ sign)
|
||||
*/
|
||||
public Set<String> extractMentionedScreennames(final String text) {
|
||||
return extractMentionedScreennames(text, true);
|
||||
}
|
||||
|
||||
public Set<String> extractMentionedScreennames(final String text, final boolean exclude_duplicate) {
|
||||
if (text == null || text.length() == 0) return Collections.emptySet();
|
||||
|
||||
final Set<String> extracted = new HashSet<String>();
|
||||
for (final Entity entity : extractMentionedScreennamesWithIndices(text)) {
|
||||
if (!exclude_duplicate || !extracted.contains(entity.value)) {
|
||||
extracted.add(entity.value);
|
||||
}
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract @username references from Tweet text. A mention is an occurance
|
||||
* of @username anywhere in a Tweet.
|
||||
*
|
||||
* @param text of the tweet from which to extract usernames
|
||||
* @return List of usernames referenced (without the leading @ sign)
|
||||
*/
|
||||
public List<Entity> extractMentionedScreennamesWithIndices(final String text) {
|
||||
final ArrayList<Entity> extracted = new ArrayList<Entity>();
|
||||
for (final Entity entity : extractMentionsOrListsWithIndices(text)) {
|
||||
if (entity.listSlug == null) {
|
||||
extracted.add(entity);
|
||||
}
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
public List<Entity> extractMentionsOrListsWithIndices(final String text) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
// Performance optimization.
|
||||
// If text doesn't contain @ at all, the text doesn't
|
||||
// contain @mention. So we can simply return an empty list.
|
||||
boolean found = false;
|
||||
for (final char c : text.toCharArray()) {
|
||||
if (c == '@' || c == FULLWIDTH_AT_SIGN) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return Collections.emptyList();
|
||||
|
||||
final ArrayList<Entity> extracted = new ArrayList<Entity>();
|
||||
final Matcher matcher = Regex.VALID_MENTION_OR_LIST.matcher(text);
|
||||
while (matcher.find()) {
|
||||
final String after = text.substring(matcher.end());
|
||||
if (!Regex.INVALID_MENTION_MATCH_END.matcher(after).find()) {
|
||||
if (matcher.group(Regex.VALID_MENTION_OR_LIST_GROUP_LIST) == null) {
|
||||
extracted.add(new Entity(matcher, Entity.Type.MENTION, Regex.VALID_MENTION_OR_LIST_GROUP_USERNAME));
|
||||
} else {
|
||||
extracted.add(new Entity(matcher.start(Regex.VALID_MENTION_OR_LIST_GROUP_USERNAME) - 1, matcher
|
||||
.end(Regex.VALID_MENTION_OR_LIST_GROUP_LIST), matcher
|
||||
.group(Regex.VALID_MENTION_OR_LIST_GROUP_USERNAME), matcher
|
||||
.group(Regex.VALID_MENTION_OR_LIST_GROUP_LIST), Entity.Type.MENTION));
|
||||
}
|
||||
}
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a @username reference from the beginning of Tweet text. A reply
|
||||
* is an occurance of @username at the beginning of a Tweet, preceded by 0
|
||||
* or more spaces.
|
||||
*
|
||||
* @param text of the tweet from which to extract the replied to username
|
||||
* @return username referenced, if any (without the leading @ sign). Returns
|
||||
* null if this is not a reply.
|
||||
*/
|
||||
public String extractReplyScreenname(final String text) {
|
||||
if (text == null) return null;
|
||||
|
||||
final Matcher matcher = Regex.VALID_REPLY.matcher(text);
|
||||
if (matcher.find()) {
|
||||
final String after = text.substring(matcher.end());
|
||||
if (Regex.INVALID_MENTION_MATCH_END.matcher(after).find())
|
||||
return null;
|
||||
else
|
||||
return matcher.group(Regex.VALID_REPLY_GROUP_USERNAME);
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract URL references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract URLs
|
||||
* @return List of URLs referenced.
|
||||
*/
|
||||
public List<String> extractURLs(final String text) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
final ArrayList<String> urls = new ArrayList<String>();
|
||||
for (final Entity entity : extractURLsWithIndices(text)) {
|
||||
urls.add(entity.value);
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract URL references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract URLs
|
||||
* @return List of URLs referenced.
|
||||
*/
|
||||
public List<Entity> extractURLsWithIndices(final String text) {
|
||||
if (text == null || text.length() == 0
|
||||
|| (extractURLWithoutProtocol ? text.indexOf('.') : text.indexOf(':')) == -1) // Performance
|
||||
// optimization.
|
||||
// If text doesn't contain '.' or ':' at all, text doesn't contain
|
||||
// URL,
|
||||
// so we can simply return an empty list.
|
||||
return Collections.emptyList();
|
||||
|
||||
final ArrayList<Entity> urls = new ArrayList<Entity>();
|
||||
|
||||
final Matcher matcher = Regex.VALID_URL.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (matcher.group(Regex.VALID_URL_GROUP_PROTOCOL) == null) {
|
||||
// skip if protocol is not present and
|
||||
// 'extractURLWithoutProtocol' is false
|
||||
// or URL is preceded by invalid character.
|
||||
if (!extractURLWithoutProtocol
|
||||
|| Regex.INVALID_URL_WITHOUT_PROTOCOL_MATCH_BEGIN.matcher(
|
||||
matcher.group(Regex.VALID_URL_GROUP_BEFORE)).matches()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String url = matcher.group(Regex.VALID_URL_GROUP_URL);
|
||||
final int start = matcher.start(Regex.VALID_URL_GROUP_URL);
|
||||
int end = matcher.end(Regex.VALID_URL_GROUP_URL);
|
||||
final Matcher tco_matcher = Regex.VALID_TCO_URL.matcher(url);
|
||||
if (tco_matcher.find()) {
|
||||
// In the case of t.co URLs, don't allow additional path
|
||||
// characters.
|
||||
url = tco_matcher.group();
|
||||
end = start + url.length();
|
||||
}
|
||||
|
||||
urls.add(new Entity(start, end, url, Entity.Type.URL));
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
public boolean isExtractURLWithoutProtocol() {
|
||||
return extractURLWithoutProtocol;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify Unicode-based indices of the entities to UTF-16 based indices.
|
||||
*
|
||||
* In UTF-16 based indices, Unicode supplementary characters are counted as
|
||||
* two characters.
|
||||
*
|
||||
* This method requires that the list of entities be in ascending order by
|
||||
* start index.
|
||||
*
|
||||
* @param text original text
|
||||
*
|
||||
* @param entities entities with Unicode based indices
|
||||
*/
|
||||
public void modifyIndicesFromUnicodeToUTF16(final String text, final List<Entity> entities) {
|
||||
final IndexConverter convert = new IndexConverter(text);
|
||||
|
||||
for (final Entity entity : entities) {
|
||||
entity.start = convert.codePointsToCodeUnits(entity.start);
|
||||
entity.end = convert.codePointsToCodeUnits(entity.end);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify UTF-16-based indices of the entities to Unicode-based indices.
|
||||
*
|
||||
* In Unicode-based indices, Unicode supplementary characters are counted as
|
||||
* single characters.
|
||||
*
|
||||
* This method requires that the list of entities be in ascending order by
|
||||
* start index.
|
||||
*
|
||||
* @param text original text
|
||||
*
|
||||
* @param entities entities with UTF-16 based indices
|
||||
*/
|
||||
public void modifyIndicesFromUTF16ToToUnicode(final String text, final List<Entity> entities) {
|
||||
final IndexConverter convert = new IndexConverter(text);
|
||||
|
||||
for (final Entity entity : entities) {
|
||||
entity.start = convert.codeUnitsToCodePoints(entity.start);
|
||||
entity.end = convert.codeUnitsToCodePoints(entity.end);
|
||||
}
|
||||
}
|
||||
|
||||
public void setExtractURLWithoutProtocol(final boolean extractURLWithoutProtocol) {
|
||||
this.extractURLWithoutProtocol = extractURLWithoutProtocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract #hashtag references from Tweet text.
|
||||
*
|
||||
* @param text of the tweet from which to extract hashtags
|
||||
* @param checkUrlOverlap if true, check if extracted hashtags overlap URLs
|
||||
* and remove overlapping ones
|
||||
* @return List of hashtags referenced (without the leading # sign)
|
||||
*/
|
||||
private List<Entity> extractHashtagsWithIndices(final String text, final boolean checkUrlOverlap) {
|
||||
if (text == null || text.length() == 0) return Collections.emptyList();
|
||||
|
||||
// Performance optimization.
|
||||
// If text doesn't contain # at all, text doesn't contain
|
||||
// hashtag, so we can simply return an empty list.
|
||||
boolean found = false;
|
||||
for (final char c : text.toCharArray()) {
|
||||
if (c == '#' || c == FULLWIDTH_NUMBER_SIGN) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return Collections.emptyList();
|
||||
|
||||
final ArrayList<Entity> extracted = new ArrayList<Entity>();
|
||||
final Matcher matcher = Regex.VALID_HASHTAG.matcher(text);
|
||||
|
||||
while (matcher.find()) {
|
||||
final String after = text.substring(matcher.end());
|
||||
if (!Regex.INVALID_HASHTAG_MATCH_END.matcher(after).find()) {
|
||||
extracted.add(new Entity(matcher, Entity.Type.HASHTAG, Regex.VALID_HASHTAG_GROUP_TAG));
|
||||
}
|
||||
}
|
||||
|
||||
if (checkUrlOverlap) {
|
||||
// extract URLs
|
||||
final List<Entity> urls = extractURLsWithIndices(text);
|
||||
if (!urls.isEmpty()) {
|
||||
extracted.addAll(urls);
|
||||
// remove overlap
|
||||
removeOverlappingEntities(extracted);
|
||||
// remove URL entities
|
||||
final Iterator<Entity> it = extracted.iterator();
|
||||
while (it.hasNext()) {
|
||||
final Entity entity = it.next();
|
||||
if (entity.getType() != Entity.Type.HASHTAG) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
private void removeOverlappingEntities(final List<Entity> entities) {
|
||||
// sort by index
|
||||
Collections.<Entity> sort(entities, new Comparator<Entity>() {
|
||||
@Override
|
||||
public int compare(final Entity e1, final Entity e2) {
|
||||
return e1.start - e2.start;
|
||||
}
|
||||
});
|
||||
|
||||
// Remove overlapping entities.
|
||||
// Two entities overlap only when one is URL and the other is
|
||||
// hashtag/mention
|
||||
// which is a part of the URL. When it happens, we choose URL over
|
||||
// hashtag/mention
|
||||
// by selecting the one with smaller start index.
|
||||
if (!entities.isEmpty()) {
|
||||
final Iterator<Entity> it = entities.iterator();
|
||||
Entity prev = it.next();
|
||||
while (it.hasNext()) {
|
||||
final Entity cur = it.next();
|
||||
if (prev.getEnd() > cur.getStart()) {
|
||||
it.remove();
|
||||
} else {
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Entity {
|
||||
protected int start;
|
||||
|
||||
protected int end;
|
||||
protected final String value;
|
||||
// listSlug is used to store the list portion of @mention/list.
|
||||
protected final String listSlug;
|
||||
protected final Type type;
|
||||
protected String displayURL = null;
|
||||
|
||||
protected String expandedURL = null;
|
||||
|
||||
public Entity(final int start, final int end, final String value, final String listSlug, final Type type) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.value = value;
|
||||
this.listSlug = listSlug;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Entity(final int start, final int end, final String value, final Type type) {
|
||||
this(start, end, value, null, type);
|
||||
}
|
||||
|
||||
public Entity(final Matcher matcher, final Type type, final int groupNumber) {
|
||||
// Offset -1 on start index to include @, # symbols for mentions and
|
||||
// hashtags
|
||||
this(matcher, type, groupNumber, -1);
|
||||
}
|
||||
|
||||
public Entity(final Matcher matcher, final Type type, final int groupNumber, final int startOffset) {
|
||||
this(matcher.start(groupNumber) + startOffset, matcher.end(groupNumber), matcher.group(groupNumber), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) return true;
|
||||
|
||||
if (!(obj instanceof Entity)) return false;
|
||||
|
||||
final Entity other = (Entity) obj;
|
||||
|
||||
if (type.equals(other.type) && start == other.start && end == other.end && value.equals(other.value))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDisplayURL() {
|
||||
return displayURL;
|
||||
}
|
||||
|
||||
public Integer getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public String getExpandedURL() {
|
||||
return expandedURL;
|
||||
}
|
||||
|
||||
public String getListSlug() {
|
||||
return listSlug;
|
||||
}
|
||||
|
||||
public Integer getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return type.hashCode() + value.hashCode() + start + end;
|
||||
}
|
||||
|
||||
public void setDisplayURL(final String displayURL) {
|
||||
this.displayURL = displayURL;
|
||||
}
|
||||
|
||||
public void setExpandedURL(final String expandedURL) {
|
||||
this.expandedURL = expandedURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value + "(" + type + ") [" + start + "," + end + "]";
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
URL, HASHTAG, MENTION, CASHTAG
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An efficient converter of indices between code points and code units.
|
||||
*/
|
||||
private static final class IndexConverter {
|
||||
protected final String text;
|
||||
|
||||
// Keep track of a single corresponding pair of code unit and code point
|
||||
// offsets so that we can re-use counting work if the next requested
|
||||
// entity is near the most recent entity.
|
||||
protected int codePointIndex = 0;
|
||||
protected int charIndex = 0;
|
||||
|
||||
IndexConverter(final String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param codePointIndex Index into the string measured in code points.
|
||||
* @return the code unit index that corresponds to the specified code
|
||||
* point index.
|
||||
*/
|
||||
int codePointsToCodeUnits(final int codePointIndex) {
|
||||
// Note that offsetByCodePoints accepts negative indices.
|
||||
charIndex = text.offsetByCodePoints(charIndex, codePointIndex - this.codePointIndex);
|
||||
this.codePointIndex = codePointIndex;
|
||||
return charIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param charIndex Index into the string measured in code units.
|
||||
* @return The code point index that corresponds to the specified
|
||||
* character index.
|
||||
*/
|
||||
int codeUnitsToCodePoints(final int charIndex) {
|
||||
if (charIndex < this.charIndex) {
|
||||
codePointIndex -= text.codePointCount(charIndex, this.charIndex);
|
||||
} else {
|
||||
codePointIndex += text.codePointCount(this.charIndex, charIndex);
|
||||
}
|
||||
this.charIndex = charIndex;
|
||||
|
||||
// Make sure that charIndex never points to the second code unit of
|
||||
// a
|
||||
// surrogate pair.
|
||||
if (charIndex > 0 && Character.isSupplementaryCodePoint(text.codePointAt(charIndex - 1))) {
|
||||
this.charIndex -= 1;
|
||||
}
|
||||
return codePointIndex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
package com.twitter;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
//@formatter:off
|
||||
public class Regex {
|
||||
private static final String UNICODE_SPACES = "[" +
|
||||
"\\u0009-\\u000d" + // # White_Space # Cc [5] <control-0009>..<control-000D>
|
||||
"\\u0020" + // White_Space # Zs SPACE
|
||||
"\\u0085" + // White_Space # Cc <control-0085>
|
||||
"\\u00a0" + // White_Space # Zs NO-BREAK SPACE
|
||||
"\\u1680" + // White_Space # Zs OGHAM SPACE MARK
|
||||
"\\u180E" + // White_Space # Zs MONGOLIAN VOWEL SEPARATOR
|
||||
"\\u2000-\\u200a" + // # White_Space # Zs [11] EN QUAD..HAIR SPACE
|
||||
"\\u2028" + // White_Space # Zl LINE SEPARATOR
|
||||
"\\u2029" + // White_Space # Zp PARAGRAPH SEPARATOR
|
||||
"\\u202F" + // White_Space # Zs NARROW NO-BREAK SPACE
|
||||
"\\u205F" + // White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
||||
"\\u3000" + // White_Space # Zs IDEOGRAPHIC SPACE
|
||||
"]";
|
||||
|
||||
private static String LATIN_ACCENTS_CHARS = "\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u00ff" + // Latin-1
|
||||
"\\u0100-\\u024f" + // Latin Extended A and B
|
||||
"\\u0253\\u0254\\u0256\\u0257\\u0259\\u025b\\u0263\\u0268\\u026f\\u0272\\u0289\\u028b" + // IPA Extensions
|
||||
"\\u02bb" + // Hawaiian
|
||||
"\\u0300-\\u036f" + // Combining diacritics
|
||||
"\\u1e00-\\u1eff"; // Latin Extended Additional (mostly for Vietnamese)
|
||||
private static final String HASHTAG_ALPHA_CHARS = "a-z" + LATIN_ACCENTS_CHARS +
|
||||
"\\u0400-\\u04ff\\u0500-\\u0527" + // Cyrillic
|
||||
"\\u2de0-\\u2dff\\ua640-\\ua69f" + // Cyrillic Extended A/B
|
||||
"\\u0591-\\u05bf\\u05c1-\\u05c2\\u05c4-\\u05c5\\u05c7" +
|
||||
"\\u05d0-\\u05ea\\u05f0-\\u05f4" + // Hebrew
|
||||
"\\ufb1d-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40-\\ufb41" +
|
||||
"\\ufb43-\\ufb44\\ufb46-\\ufb4f" + // Hebrew Pres. Forms
|
||||
"\\u0610-\\u061a\\u0620-\\u065f\\u066e-\\u06d3\\u06d5-\\u06dc" +
|
||||
"\\u06de-\\u06e8\\u06ea-\\u06ef\\u06fa-\\u06fc\\u06ff" + // Arabic
|
||||
"\\u0750-\\u077f\\u08a0\\u08a2-\\u08ac\\u08e4-\\u08fe" + // Arabic Supplement and Extended A
|
||||
"\\ufb50-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb" + // Pres. Forms A
|
||||
"\\ufe70-\\ufe74\\ufe76-\\ufefc" + // Pres. Forms B
|
||||
"\\u200c" + // Zero-Width Non-Joiner
|
||||
"\\u0e01-\\u0e3a\\u0e40-\\u0e4e" + // Thai
|
||||
"\\u1100-\\u11ff\\u3130-\\u3185\\uA960-\\uA97F\\uAC00-\\uD7AF\\uD7B0-\\uD7FF" + // Hangul (Korean)
|
||||
"\\p{InHiragana}\\p{InKatakana}" + // Japanese Hiragana and Katakana
|
||||
"\\p{InCJKUnifiedIdeographs}" + // Japanese Kanji / Chinese Han
|
||||
"\\u3003\\u3005\\u303b" + // Kanji/Han iteration marks
|
||||
"\\uff21-\\uff3a\\uff41-\\uff5a" + // full width Alphabet
|
||||
"\\uff66-\\uff9f" + // half width Katakana
|
||||
"\\uffa1-\\uffdc"; // half width Hangul (Korean)
|
||||
private static final String HASHTAG_ALPHA_NUMERIC_CHARS = "0-9\\uff10-\\uff19_" + HASHTAG_ALPHA_CHARS;
|
||||
private static final String HASHTAG_ALPHA = "[" + HASHTAG_ALPHA_CHARS +"]";
|
||||
private static final String HASHTAG_ALPHA_NUMERIC = "[" + HASHTAG_ALPHA_NUMERIC_CHARS +"]";
|
||||
|
||||
/**
|
||||
* Fullwidth at sign: '@'
|
||||
*/
|
||||
private static final char FULLWIDTH_AT_SIGN = '\uff20';
|
||||
|
||||
/**
|
||||
* Fullwidth number sign: '#'
|
||||
*/
|
||||
private static final char FULLWIDTH_NUMBER_SIGN = '\uff03';
|
||||
|
||||
/* URL related hash regex collection */
|
||||
private static final String URL_VALID_PRECEEDING_CHARS = "(?:[^A-Z0-9@"+FULLWIDTH_AT_SIGN+"$#"+FULLWIDTH_NUMBER_SIGN+"\u202A-\u202E]|^)";
|
||||
|
||||
private static final String URL_VALID_CHARS = "[\\p{Alnum}" + LATIN_ACCENTS_CHARS + "]";
|
||||
private static final String URL_VALID_SUBDOMAIN = "(?:(?:" + URL_VALID_CHARS + "[" + URL_VALID_CHARS + "\\-_]*)?" + URL_VALID_CHARS + "\\.)";
|
||||
private static final String URL_VALID_DOMAIN_NAME = "(?:(?:" + URL_VALID_CHARS + "[" + URL_VALID_CHARS + "\\-]*)?" + URL_VALID_CHARS + "\\.)";
|
||||
/* Any non-space, non-punctuation characters. \p{Z} = any kind of whitespace or invisible separator. */
|
||||
private static final String URL_VALID_UNICODE_CHARS = "[.[^\\p{Punct}\\s\\p{Z}\\p{InGeneralPunctuation}]]";
|
||||
|
||||
private static final String URL_VALID_GTLD =
|
||||
"(?:(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx)(?=\\P{Alnum}|$))";
|
||||
private static final String URL_VALID_CCTLD =
|
||||
"(?:(?:ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|" +
|
||||
"bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|" +
|
||||
"er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|" +
|
||||
"hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|" +
|
||||
"lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|" +
|
||||
"nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|" +
|
||||
"sl|sm|sn|so|sr|ss|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|" +
|
||||
"va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)(?=\\P{Alnum}|$))";
|
||||
private static final String URL_PUNYCODE = "(?:xn--[0-9a-z]+)";
|
||||
|
||||
private static final String URL_VALID_DOMAIN =
|
||||
"(?:" + // subdomains + domain + TLD
|
||||
URL_VALID_SUBDOMAIN + "+" + URL_VALID_DOMAIN_NAME + // e.g. www.twitter.com, foo.co.jp, bar.co.uk
|
||||
"(?:" + URL_VALID_GTLD + "|" + URL_VALID_CCTLD + "|" + URL_PUNYCODE + ")" +
|
||||
")" +
|
||||
"|(?:" + // domain + gTLD
|
||||
URL_VALID_DOMAIN_NAME + // e.g. twitter.com
|
||||
"(?:" + URL_VALID_GTLD + "|" + URL_PUNYCODE + ")" +
|
||||
")" +
|
||||
"|(?:" + "(?<=https?://)" +
|
||||
"(?:" +
|
||||
"(?:" + URL_VALID_DOMAIN_NAME + URL_VALID_CCTLD + ")" + // protocol + domain + ccTLD
|
||||
"|(?:" +
|
||||
URL_VALID_UNICODE_CHARS + "+\\." + // protocol + unicode domain + TLD
|
||||
"(?:" + URL_VALID_GTLD + "|" + URL_VALID_CCTLD + ")" +
|
||||
")" +
|
||||
")" +
|
||||
")" +
|
||||
"|(?:" + // domain + ccTLD + '/'
|
||||
URL_VALID_DOMAIN_NAME + URL_VALID_CCTLD + "(?=/)" + // e.g. t.co/
|
||||
")";
|
||||
|
||||
private static final String URL_VALID_PORT_NUMBER = "[0-9]++";
|
||||
|
||||
private static final String URL_VALID_GENERAL_PATH_CHARS = "[a-z0-9!\\*';:=\\+,.\\$/%#\\[\\]\\-_~\\|&" + LATIN_ACCENTS_CHARS + "]";
|
||||
/** Allow URL paths to contain balanced parens
|
||||
* 1. Used in Wikipedia URLs like /Primer_(film)
|
||||
* 2. Used in IIS sessions like /S(dfd346)/
|
||||
**/
|
||||
private static final String URL_BALANCED_PARENS = "\\(" + URL_VALID_GENERAL_PATH_CHARS + "+\\)";
|
||||
/** Valid end-of-path chracters (so /foo. does not gobble the period).
|
||||
* 2. Allow =&# for empty URL parameters and other URL-join artifacts
|
||||
**/
|
||||
private static final String URL_VALID_PATH_ENDING_CHARS = "[a-z0-9=_#/\\-\\+" + LATIN_ACCENTS_CHARS + "]|(?:" + URL_BALANCED_PARENS +")";
|
||||
|
||||
private static final String URL_VALID_PATH = "(?:" +
|
||||
"(?:" +
|
||||
URL_VALID_GENERAL_PATH_CHARS + "*" +
|
||||
"(?:" + URL_BALANCED_PARENS + URL_VALID_GENERAL_PATH_CHARS + "*)*" +
|
||||
URL_VALID_PATH_ENDING_CHARS +
|
||||
")|(?:@" + URL_VALID_GENERAL_PATH_CHARS + "+/)" +
|
||||
")";
|
||||
|
||||
private static final String URL_VALID_URL_QUERY_CHARS = "[a-z0-9!?\\*'\\(\\);:&=\\+\\$/%#\\[\\]\\-_\\.,~\\|]";
|
||||
private static final String URL_VALID_URL_QUERY_ENDING_CHARS = "[a-z0-9_&=#/]";
|
||||
private static final String VALID_URL_PATTERN_STRING =
|
||||
"(" + // $1 total match
|
||||
"(" + URL_VALID_PRECEEDING_CHARS + ")" + // $2 Preceeding chracter
|
||||
"(" + // $3 URL
|
||||
"(https?://)?" + // $4 Protocol (optional)
|
||||
"(" + URL_VALID_DOMAIN + ")" + // $5 Domain(s)
|
||||
"(?::(" + URL_VALID_PORT_NUMBER +"))?" + // $6 Port number (optional)
|
||||
"(/" +
|
||||
URL_VALID_PATH + "*+" +
|
||||
")?" + // $7 URL Path and anchor
|
||||
"(\\?" + URL_VALID_URL_QUERY_CHARS + "*" + // $8 Query String
|
||||
URL_VALID_URL_QUERY_ENDING_CHARS + ")?" +
|
||||
")" +
|
||||
")";
|
||||
|
||||
private static String AT_SIGNS_CHARS = "@\uFF20";
|
||||
|
||||
private static final String DOLLAR_SIGN_CHAR = "\\$";
|
||||
private static final String CASHTAG = "[a-z]{1,6}(?:[._][a-z]{1,2})?";
|
||||
|
||||
/* Begin public constants */
|
||||
|
||||
public static final Pattern VALID_HASHTAG = Pattern.compile("(^|[^&" + HASHTAG_ALPHA_NUMERIC_CHARS + "])((#|\uFF03)(" + HASHTAG_ALPHA_NUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHA_NUMERIC + "*))", Pattern.CASE_INSENSITIVE);
|
||||
public static final int VALID_HASHTAG_GROUP_BEFORE = 1;
|
||||
public static final int VALID_HASHTAG_GROUP_HASHTAG_FULL = 2;
|
||||
public static final int VALID_HASHTAG_GROUP_HASH = 3;
|
||||
public static final int VALID_HASHTAG_GROUP_TAG = 4;
|
||||
public static final Pattern INVALID_HASHTAG_MATCH_END = Pattern.compile("^(?:[#"+FULLWIDTH_NUMBER_SIGN+"]|://)");
|
||||
|
||||
public static final Pattern AT_SIGNS = Pattern.compile("[" + AT_SIGNS_CHARS + "]");
|
||||
public static final Pattern VALID_MENTION_OR_LIST = Pattern.compile("([^a-z0-9_!#$%&*" + AT_SIGNS_CHARS + "]|^|RT:?)(" + AT_SIGNS + "+)([a-z0-9_]{1,20})(\\/([a-z][a-z0-9_\\-]{0,24}))?", Pattern.CASE_INSENSITIVE);
|
||||
public static final int VALID_MENTION_OR_LIST_GROUP_BEFORE = 1;
|
||||
public static final int VALID_MENTION_OR_LIST_GROUP_AT = 2;
|
||||
public static final int VALID_MENTION_OR_LIST_GROUP_USERNAME = 3;
|
||||
public static final int VALID_MENTION_OR_LIST_GROUP_LIST_WITH_SLASH = 4;
|
||||
public static final int VALID_MENTION_OR_LIST_GROUP_LIST = 5;
|
||||
|
||||
public static final Pattern VALID_REPLY = Pattern.compile("^(?:" + UNICODE_SPACES + ")*" + AT_SIGNS + "([a-z0-9_]{1,20})", Pattern.CASE_INSENSITIVE);
|
||||
public static final int VALID_REPLY_GROUP_USERNAME = 1;
|
||||
|
||||
public static final Pattern INVALID_MENTION_MATCH_END = Pattern.compile("^(?:[" + AT_SIGNS_CHARS + LATIN_ACCENTS_CHARS + "]|://)");
|
||||
|
||||
public static final Pattern VALID_URL = Pattern.compile(VALID_URL_PATTERN_STRING, Pattern.CASE_INSENSITIVE);
|
||||
public static final int VALID_URL_GROUP_ALL = 1;
|
||||
public static final int VALID_URL_GROUP_BEFORE = 2;
|
||||
public static final int VALID_URL_GROUP_URL = 3;
|
||||
public static final int VALID_URL_GROUP_PROTOCOL = 4;
|
||||
public static final int VALID_URL_GROUP_DOMAIN = 5;
|
||||
public static final int VALID_URL_GROUP_PORT = 6;
|
||||
public static final int VALID_URL_GROUP_PATH = 7;
|
||||
public static final int VALID_URL_GROUP_QUERY_STRING = 8;
|
||||
|
||||
public static final Pattern VALID_TCO_URL = Pattern.compile("^https?:\\/\\/t\\.co\\/[a-z0-9]+", Pattern.CASE_INSENSITIVE);
|
||||
public static final Pattern INVALID_URL_WITHOUT_PROTOCOL_MATCH_BEGIN = Pattern.compile("[-_./]$");
|
||||
|
||||
public static final Pattern VALID_CASHTAG = Pattern.compile("((?:^|" + UNICODE_SPACES + ")((" + DOLLAR_SIGN_CHAR + ")(" + CASHTAG + "))" +"(?=$|\\s|\\p{Punct}))", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final int VALID_CASHTAG_GROUP_CASHTAG_FULL = 2;
|
||||
public static final int VALID_CASHTAG_GROUP_CASH = 3;
|
||||
public static final int VALID_CASHTAG_GROUP_TAG = 4;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package com.twitter;
|
||||
|
||||
/**
|
||||
* A class for validating Tweet texts.
|
||||
*/
|
||||
public class Validator {
|
||||
public static final int MAX_TWEET_LENGTH = 140;
|
||||
|
||||
protected int shortUrlLength = 22;
|
||||
protected int shortUrlLengthHttps = 23;
|
||||
|
||||
private final Extractor extractor = new Extractor();
|
||||
|
||||
public int getShortUrlLength() {
|
||||
return shortUrlLength;
|
||||
}
|
||||
|
||||
public int getShortUrlLengthHttps() {
|
||||
return shortUrlLengthHttps;
|
||||
}
|
||||
|
||||
public int getTweetLength(final String text) {
|
||||
int length = text.codePointCount(0, text.length());
|
||||
|
||||
for (final Extractor.Entity urlEntity : extractor.extractURLsWithIndices(text)) {
|
||||
length += urlEntity.start - urlEntity.end;
|
||||
length += urlEntity.value.toLowerCase().startsWith("https://") ? shortUrlLengthHttps : shortUrlLength;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public boolean isValidTweet(final String text) {
|
||||
if (text == null || text.length() == 0) return false;
|
||||
|
||||
for (final char c : text.toCharArray()) {
|
||||
if (c == '\uFFFE' || c == '\uuFEFF' || // BOM
|
||||
c == '\uFFFF' || // Special
|
||||
c >= '\u202A' && c <= '\u202E') return false;
|
||||
}
|
||||
|
||||
return getTweetLength(text) <= MAX_TWEET_LENGTH;
|
||||
}
|
||||
|
||||
public void setShortUrlLength(final int shortUrlLength) {
|
||||
this.shortUrlLength = shortUrlLength;
|
||||
}
|
||||
|
||||
public void setShortUrlLengthHttps(final int shortUrlLengthHttps) {
|
||||
this.shortUrlLengthHttps = shortUrlLengthHttps;
|
||||
}
|
||||
}
|
|
@ -1,60 +1,63 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="160dp">
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="160dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/title_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
<LinearLayout
|
||||
android:id="@+id/title_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingLeft="8dp"
|
||||
android:text="@string/title"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="0dp"
|
||||
android:paddingStart="8dp"
|
||||
android:text="@string/title"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/action_progress_layout"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="match_parent">
|
||||
<FrameLayout
|
||||
android:id="@+id/action_progress_layout"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/list_selector_holo_dark"
|
||||
android:onClick="onClick"
|
||||
android:src="@drawable/ic_menu_send"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
<ImageButton
|
||||
android:id="@+id/action"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/list_selector_holo_dark"
|
||||
android:onClick="onClick"
|
||||
android:src="@drawable/ic_menu_send"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/title_layout"
|
||||
android:padding="12dp">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/title_layout"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
</ScrollView>
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
</ScrollView>
|
||||
|
||||
</RelativeLayout>
|
|
@ -99,7 +99,6 @@ dependencies {
|
|||
compile 'commons-primitives:commons-primitives:1.0'
|
||||
compile 'com.bluelinelabs:logansquare:1.3.4'
|
||||
compile 'org.jraf:android-switch-backport:2.0.1'
|
||||
compile 'com.github.mariotaku:jackson-jr-trees:7fe682ee09'
|
||||
compile 'com.makeramen:roundedimageview:2.1.1'
|
||||
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2'
|
||||
|
@ -111,7 +110,7 @@ dependencies {
|
|||
compile 'org.attoparser:attoparser:1.4.0.RELEASE'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.10'
|
||||
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.10'
|
||||
compile 'com.github.mariotaku.SQLiteQB:library:0.9.5-SNAPSHOT'
|
||||
compile 'com.github.mariotaku.SQLiteQB:library:0.9.5'
|
||||
compile 'com.github.mariotaku.ObjectCursor:core:0.9.4'
|
||||
compile project(':twidere.component.common')
|
||||
compile project(':twidere.component.nyan')
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
|
||||
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
|
||||
|
||||
<application>
|
||||
<application
|
||||
android:fullBackupContent="true"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
android:value="@integer/google_play_services_version"/>
|
||||
<meta-data
|
||||
android:name="com.google.android.maps.v2.API_KEY"
|
||||
android:value="AIzaSyCVdCIMFFxdNqHnCPrJ9yKUzoTfs8jhYGc" />
|
||||
android:value="AIzaSyCVdCIMFFxdNqHnCPrJ9yKUzoTfs8jhYGc"/>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -43,7 +43,6 @@ import org.mariotaku.twidere.util.ActivityTracker;
|
|||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||
import org.mariotaku.twidere.util.StrictModeUtils;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
import org.mariotaku.twidere.util.TwidereColorUtils;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||
import org.mariotaku.twidere.util.support.ViewSupport;
|
||||
|
@ -249,7 +248,7 @@ public abstract class BasePreferenceActivity extends AppCompatPreferenceActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(int resid) {
|
||||
public void setTheme(int resId) {
|
||||
super.setTheme(mCurrentThemeResource = getThemeResourceId());
|
||||
if (shouldApplyWindowBackground()) {
|
||||
ThemeUtils.applyWindowBackground(this, getWindow(), mCurrentThemeResource,
|
||||
|
@ -258,13 +257,13 @@ public abstract class BasePreferenceActivity extends AppCompatPreferenceActivity
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(@NonNull Resources.Theme theme, int resid, boolean first) {
|
||||
protected void onApplyThemeResource(@NonNull Resources.Theme theme, int resId, boolean first) {
|
||||
mCurrentThemeColor = getThemeColor();
|
||||
mCurrentThemeBackgroundAlpha = getThemeBackgroundAlpha();
|
||||
mProfileImageStyle = Utils.getProfileImageStyle(this);
|
||||
mCurrentThemeBackgroundOption = getThemeBackgroundOption();
|
||||
mCurrentThemeFontFamily = getThemeFontFamily();
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
super.onApplyThemeResource(theme, resId, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -91,7 +91,7 @@ public class ImageCropperActivity extends CropImageActivity implements IThemedAc
|
|||
|
||||
|
||||
@Override
|
||||
public void setTheme(final int resid) {
|
||||
public void setTheme(final int resId) {
|
||||
super.setTheme(mCurrentThemeResource = getThemeResourceId());
|
||||
ThemeUtils.applyWindowBackground(this, getWindow(), mCurrentThemeResource,
|
||||
mCurrentThemeBackgroundOption, mCurrentThemeBackgroundAlpha);
|
||||
|
|
|
@ -44,7 +44,7 @@ public class MainActivity extends Activity implements Constants {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(int resid) {
|
||||
public void setTheme(int resId) {
|
||||
super.setTheme(ThemeUtils.getNoActionBarThemeResource(this));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.mariotaku.twidere.activity.support;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
|
|
|
@ -82,7 +82,6 @@ import org.mariotaku.twidere.model.ParcelableAccount;
|
|||
import org.mariotaku.twidere.model.SupportTabSpec;
|
||||
import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
|
||||
import org.mariotaku.twidere.model.message.UnreadCountUpdatedEvent;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
|
|
|
@ -110,12 +110,13 @@ public abstract class ThemedAppCompatActivity extends AppCompatActivity implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSupportActionModeStarted(android.support.v7.view.ActionMode mode) {
|
||||
public void onSupportActionModeStarted(@NonNull android.support.v7.view.ActionMode mode) {
|
||||
super.onSupportActionModeStarted(mode);
|
||||
ThemeUtils.applySupportActionModeColor(mode, this, getCurrentThemeResourceId(),
|
||||
getCurrentThemeColor(), getThemeBackgroundOption(), true);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ThemedAppCompatDelegateFactory.ThemedAppCompatDelegate getDelegate() {
|
||||
if (mDelegate != null) return mDelegate;
|
||||
|
|
|
@ -19,22 +19,24 @@
|
|||
|
||||
package org.mariotaku.twidere.api.twitter;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.jr.tree.JacksonJrSimpleTreeCodec;
|
||||
|
||||
import org.mariotaku.restfu.callback.RawCallback;
|
||||
import org.mariotaku.restfu.http.HttpResponse;
|
||||
import org.mariotaku.twidere.api.twitter.model.DeletionEvent;
|
||||
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
|
||||
import org.mariotaku.twidere.api.twitter.model.Status;
|
||||
import org.mariotaku.twidere.api.twitter.model.StatusDeletionNotice;
|
||||
import org.mariotaku.twidere.api.twitter.model.StatusFavoriteEvent;
|
||||
import org.mariotaku.twidere.api.twitter.model.TwitterStreamObject;
|
||||
import org.mariotaku.twidere.api.twitter.model.TwitterStreamObject.Type;
|
||||
import org.mariotaku.twidere.api.twitter.model.User;
|
||||
import org.mariotaku.twidere.api.twitter.model.UserList;
|
||||
import org.mariotaku.twidere.api.twitter.model.Warning;
|
||||
import org.mariotaku.twidere.api.twitter.util.CRLFLineReader;
|
||||
import org.mariotaku.twidere.api.twitter.util.JSONObjectType;
|
||||
import org.mariotaku.twidere.util.LoganSquareMapperFinder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -57,7 +59,6 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
onException(cause);
|
||||
return;
|
||||
}
|
||||
final JacksonJrSimpleTreeCodec mapper = new JacksonJrSimpleTreeCodec();
|
||||
final CRLFLineReader reader = new CRLFLineReader(new InputStreamReader(response.getBody().stream(), "UTF-8"));
|
||||
try {
|
||||
for (String line; (line = reader.readLine()) != null && !disconnected; ) {
|
||||
|
@ -65,74 +66,78 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
onConnected();
|
||||
connected = true;
|
||||
}
|
||||
if (line.isEmpty()) continue;
|
||||
TreeNode rootNode = mapper.readTree(LoganSquare.JSON_FACTORY.createParser(line));
|
||||
switch (JSONObjectType.determine(rootNode)) {
|
||||
case SENDER: {
|
||||
if (TextUtils.isEmpty(line)) continue;
|
||||
final TwitterStreamObject object = LoganSquare.parse(line, TwitterStreamObject.class);
|
||||
switch (object.determine()) {
|
||||
case Type.SENDER: {
|
||||
break;
|
||||
}
|
||||
case STATUS: {
|
||||
onStatus(LoganSquareMapperFinder.mapperFor(Status.class).parse(rootNode.traverse()));
|
||||
case Type.STATUS: {
|
||||
onStatus(LoganSquareMapperFinder.mapperFor(Status.class).parse(line));
|
||||
break;
|
||||
}
|
||||
case DIRECT_MESSAGE: {
|
||||
onDirectMessage(LoganSquareMapperFinder.mapperFor(DirectMessage.class).parse(rootNode.traverse()));
|
||||
case Type.DIRECT_MESSAGE: {
|
||||
onDirectMessage(object.getDirectMessage());
|
||||
break;
|
||||
}
|
||||
case DELETE: {
|
||||
case Type.DELETE: {
|
||||
final TwitterStreamObject.Delete delete = object.getDelete();
|
||||
if (delete.getStatus() != null) {
|
||||
onStatusDeleted(delete.getStatus());
|
||||
} else if (delete.getDirectMessage() != null) {
|
||||
onDirectMessageDeleted(delete.getDirectMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LIMIT:
|
||||
case Type.LIMIT:
|
||||
break;
|
||||
case STALL_WARNING:
|
||||
case Type.STALL_WARNING:
|
||||
break;
|
||||
case SCRUB_GEO:
|
||||
case Type.SCRUB_GEO:
|
||||
break;
|
||||
case FRIENDS:
|
||||
case Type.FRIENDS:
|
||||
break;
|
||||
case FAVORITE: {
|
||||
onFavorite(parse(User.class, rootNode.get("source")),
|
||||
parse(User.class, rootNode.get("target")),
|
||||
parse(Status.class, rootNode.get("target_object")));
|
||||
case Type.FAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquareMapperFinder.mapperFor(StatusFavoriteEvent.class).parse(line);
|
||||
onFavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
break;
|
||||
}
|
||||
case UNFAVORITE: {
|
||||
onUnfavorite(parse(User.class, rootNode.get("source")),
|
||||
parse(User.class, rootNode.get("target")),
|
||||
parse(Status.class, rootNode.get("target_object")));
|
||||
case Type.UNFAVORITE: {
|
||||
StatusFavoriteEvent event = LoganSquareMapperFinder.mapperFor(StatusFavoriteEvent.class).parse(line);
|
||||
onUnfavorite(event.getSource(), event.getTarget(), event.getTargetObject());
|
||||
break;
|
||||
}
|
||||
case FOLLOW:
|
||||
case Type.FOLLOW:
|
||||
break;
|
||||
case UNFOLLOW:
|
||||
case Type.UNFOLLOW:
|
||||
break;
|
||||
case USER_LIST_MEMBER_ADDED:
|
||||
case Type.USER_LIST_MEMBER_ADDED:
|
||||
break;
|
||||
case USER_LIST_MEMBER_DELETED:
|
||||
case Type.USER_LIST_MEMBER_DELETED:
|
||||
break;
|
||||
case USER_LIST_SUBSCRIBED:
|
||||
case Type.USER_LIST_SUBSCRIBED:
|
||||
break;
|
||||
case USER_LIST_UNSUBSCRIBED:
|
||||
case Type.USER_LIST_UNSUBSCRIBED:
|
||||
break;
|
||||
case USER_LIST_CREATED:
|
||||
case Type.USER_LIST_CREATED:
|
||||
break;
|
||||
case USER_LIST_UPDATED:
|
||||
case Type.USER_LIST_UPDATED:
|
||||
break;
|
||||
case USER_LIST_DESTROYED:
|
||||
case Type.USER_LIST_DESTROYED:
|
||||
break;
|
||||
case USER_UPDATE:
|
||||
case Type.USER_UPDATE:
|
||||
break;
|
||||
case USER_DELETE:
|
||||
case Type.USER_DELETE:
|
||||
break;
|
||||
case USER_SUSPEND:
|
||||
case Type.USER_SUSPEND:
|
||||
break;
|
||||
case BLOCK:
|
||||
case Type.BLOCK:
|
||||
break;
|
||||
case UNBLOCK:
|
||||
case Type.UNBLOCK:
|
||||
break;
|
||||
case DISCONNECTION:
|
||||
case Type.DISCONNECTION:
|
||||
break;
|
||||
case UNKNOWN:
|
||||
case Type.UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -161,17 +166,19 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
|
||||
public abstract void onConnected();
|
||||
|
||||
public abstract void onBlock(User source, User blockedUser);
|
||||
|
||||
public abstract void onDeletionNotice(long directMessageId, long userId);
|
||||
|
||||
public abstract void onDeletionNotice(StatusDeletionNotice statusDeletionNotice);
|
||||
public abstract void onStatus(Status status);
|
||||
|
||||
public abstract void onDirectMessage(DirectMessage directMessage);
|
||||
|
||||
public abstract void onBlock(User source, User blockedUser);
|
||||
|
||||
public abstract void onDirectMessageDeleted(DeletionEvent event);
|
||||
|
||||
public abstract void onStatusDeleted(DeletionEvent event);
|
||||
|
||||
public abstract void onException(Throwable ex);
|
||||
|
||||
public abstract void onFavorite(User source, User target, Status favoritedStatus);
|
||||
public abstract void onFavorite(User source, User target, Status targetStatus);
|
||||
|
||||
public abstract void onFollow(User source, User followedUser);
|
||||
|
||||
|
@ -181,13 +188,11 @@ public abstract class UserStreamCallback implements RawCallback {
|
|||
|
||||
public abstract void onStallWarning(Warning warn);
|
||||
|
||||
public abstract void onStatus(Status status);
|
||||
|
||||
public abstract void onTrackLimitationNotice(int numberOfLimitedStatuses);
|
||||
|
||||
public abstract void onUnblock(User source, User unblockedUser);
|
||||
|
||||
public abstract void onUnfavorite(User source, User target, Status unfavoritedStatus);
|
||||
public abstract void onUnfavorite(User source, User target, Status targetStatus);
|
||||
|
||||
public abstract void onUserListCreation(User listOwner, UserList list);
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/26.
|
||||
*/
|
||||
@JsonObject
|
||||
public class DeletionEvent {
|
||||
|
||||
@JsonField(name = "id")
|
||||
long id;
|
||||
@JsonField(name = "user_id")
|
||||
long userId;
|
||||
@JsonField(name = "timestamp_ms")
|
||||
long timestampMs;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public long getTimestampMs() {
|
||||
return timestampMs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/26.
|
||||
*/
|
||||
@JsonObject
|
||||
public class StatusFavoriteEvent {
|
||||
@JsonField(name = "source")
|
||||
User source;
|
||||
@JsonField(name = "target")
|
||||
User target;
|
||||
@JsonField(name = "target_object")
|
||||
Status targetObject;
|
||||
|
||||
public User getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public User getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Status getTargetObject() {
|
||||
return targetObject;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/1/3.
|
||||
*/
|
||||
public class StatusUtils {
|
||||
|
||||
public static Status fromParcelableStatus(@NonNull ParcelableStatus parcelable) {
|
||||
Status status = new Status();
|
||||
status.id = parcelable.id;
|
||||
status.text = InternalTwitterContentUtils.escapeTwitterStatusText(parcelable.text_plain);
|
||||
status.createdAt = new Date(parcelable.timestamp);
|
||||
status.inReplyToStatusId = parcelable.in_reply_to_status_id;
|
||||
status.inReplyToUserId = parcelable.in_reply_to_user_id;
|
||||
status.inReplyToScreenName = parcelable.in_reply_to_screen_name;
|
||||
if (parcelable.is_retweet) {
|
||||
Status retweet = status.retweetedStatus = new Status();
|
||||
retweet.id = parcelable.retweet_id;
|
||||
retweet.text = InternalTwitterContentUtils.escapeTwitterStatusText(parcelable.text_plain);
|
||||
retweet.createdAt = new Date(parcelable.retweet_timestamp);
|
||||
User retweetUser = retweet.user = new User();
|
||||
retweetUser.id = parcelable.user_id;
|
||||
retweetUser.screenName = parcelable.user_screen_name;
|
||||
retweetUser.name = parcelable.user_name;
|
||||
retweetUser.profileBackgroundImageUrl = parcelable.user_profile_image_url;
|
||||
|
||||
User user = status.user = new User();
|
||||
user.id = parcelable.retweeted_by_user_id;
|
||||
user.name = parcelable.retweeted_by_user_name;
|
||||
user.screenName = parcelable.retweeted_by_user_screen_name;
|
||||
user.profileImageUrl = parcelable.retweeted_by_user_profile_image;
|
||||
} else if (parcelable.is_quote) {
|
||||
Status quote = status.quotedStatus = new Status();
|
||||
quote.id = parcelable.quoted_id;
|
||||
quote.text = InternalTwitterContentUtils.escapeTwitterStatusText(parcelable.quoted_text_plain);
|
||||
quote.createdAt = new Date(parcelable.quoted_timestamp);
|
||||
User quotedUser = quote.user = new User();
|
||||
quotedUser.id = parcelable.quoted_user_id;
|
||||
quotedUser.name = parcelable.quoted_user_name;
|
||||
quotedUser.screenName = parcelable.quoted_user_screen_name;
|
||||
quotedUser.profileImageUrl = parcelable.quoted_user_profile_image;
|
||||
|
||||
User user = status.user = new User();
|
||||
user.id = parcelable.user_id;
|
||||
user.screenName = parcelable.user_screen_name;
|
||||
user.name = parcelable.user_name;
|
||||
user.profileBackgroundImageUrl = parcelable.user_profile_image_url;
|
||||
} else {
|
||||
User user = status.user = new User();
|
||||
user.id = parcelable.user_id;
|
||||
user.screenName = parcelable.user_screen_name;
|
||||
user.name = parcelable.user_name;
|
||||
user.profileBackgroundImageUrl = parcelable.user_profile_image_url;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package org.mariotaku.twidere.api.twitter.model;
|
||||
|
||||
import android.support.annotation.StringDef;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/26.
|
||||
*/
|
||||
@JsonObject
|
||||
public class TwitterStreamObject {
|
||||
|
||||
|
||||
@JsonField(name = "sender")
|
||||
EmptyObject sender;
|
||||
@JsonField(name = "event")
|
||||
String event;
|
||||
@JsonField(name = "text")
|
||||
String text;
|
||||
@JsonField(name = "direct_message")
|
||||
DirectMessage directMessage;
|
||||
@JsonField(name = "delete")
|
||||
Delete delete;
|
||||
@JsonField(name = "disconnect")
|
||||
EmptyObject disconnect;
|
||||
@JsonField(name = "limit")
|
||||
EmptyObject limit;
|
||||
@JsonField(name = "warning")
|
||||
EmptyObject warning;
|
||||
@JsonField(name = "scrub_geo")
|
||||
EmptyObject scrubGeo;
|
||||
@JsonField(name = "friends")
|
||||
EmptyObject friends;
|
||||
|
||||
@Type
|
||||
public String determine() {
|
||||
// This code originally lived in AbstractStreamImplementation.
|
||||
// I've moved it in here to expose it as a public encapsulation of
|
||||
// the object type determination logic.
|
||||
if (sender != null) {
|
||||
return Type.SENDER;
|
||||
} else if (text != null) {
|
||||
return Type.STATUS;
|
||||
} else if (directMessage != null) {
|
||||
return Type.DIRECT_MESSAGE;
|
||||
} else if (delete != null) {
|
||||
return Type.DELETE;
|
||||
} else if (limit != null) {
|
||||
return Type.LIMIT;
|
||||
} else if (warning != null) {
|
||||
return Type.STALL_WARNING;
|
||||
} else if (scrubGeo != null) {
|
||||
return Type.SCRUB_GEO;
|
||||
} else if (friends != null) {
|
||||
return Type.FRIENDS;
|
||||
} else if (disconnect != null) {
|
||||
return Type.DISCONNECTION;
|
||||
} else if (event != null) {
|
||||
switch (event) {
|
||||
case "favorite":
|
||||
return Type.FAVORITE;
|
||||
case "unfavorite":
|
||||
return Type.UNFAVORITE;
|
||||
case "follow":
|
||||
return Type.FOLLOW;
|
||||
case "unfollow":
|
||||
return Type.UNFOLLOW;
|
||||
case "list_member_added":
|
||||
return Type.USER_LIST_MEMBER_ADDED;
|
||||
case "list_member_removed":
|
||||
return Type.USER_LIST_MEMBER_DELETED;
|
||||
case "list_user_subscribed":
|
||||
return Type.USER_LIST_SUBSCRIBED;
|
||||
case "list_user_unsubscribed":
|
||||
return Type.USER_LIST_UNSUBSCRIBED;
|
||||
case "list_created":
|
||||
return Type.USER_LIST_CREATED;
|
||||
case "list_updated":
|
||||
return Type.USER_LIST_UPDATED;
|
||||
case "list_destroyed":
|
||||
return Type.USER_LIST_DESTROYED;
|
||||
case "user_update":
|
||||
return Type.USER_UPDATE;
|
||||
case "user_delete":
|
||||
return Type.USER_DELETE;
|
||||
case "user_suspend":
|
||||
return Type.USER_SUSPEND;
|
||||
case "block":
|
||||
return Type.BLOCK;
|
||||
case "unblock":
|
||||
return Type.UNBLOCK;
|
||||
}
|
||||
}
|
||||
return Type.UNKNOWN;
|
||||
}
|
||||
|
||||
public DirectMessage getDirectMessage() {
|
||||
return directMessage;
|
||||
}
|
||||
|
||||
public Delete getDelete() {
|
||||
return delete;
|
||||
}
|
||||
|
||||
@StringDef({Type.SENDER, Type.STATUS, Type.DIRECT_MESSAGE, Type.DELETE, Type.LIMIT,
|
||||
Type.STALL_WARNING, Type.SCRUB_GEO, Type.FRIENDS, Type.FAVORITE, Type.UNFAVORITE,
|
||||
Type.FOLLOW, Type.UNFOLLOW, Type.USER_LIST_MEMBER_ADDED, Type.USER_LIST_MEMBER_DELETED,
|
||||
Type.USER_LIST_SUBSCRIBED, Type.USER_LIST_UNSUBSCRIBED, Type.USER_LIST_CREATED,
|
||||
Type.USER_LIST_UPDATED, Type.USER_LIST_DESTROYED, Type.USER_UPDATE, Type.USER_DELETE,
|
||||
Type.USER_SUSPEND, Type.BLOCK, Type.UNBLOCK, Type.DISCONNECTION, Type.UNKNOWN})
|
||||
public @interface Type {
|
||||
String SENDER = "sender";
|
||||
String STATUS = "status";
|
||||
String DIRECT_MESSAGE = "direct_message";
|
||||
String DELETE = "delete";
|
||||
String LIMIT = "limit";
|
||||
String STALL_WARNING = "stall_warning";
|
||||
String SCRUB_GEO = "scrub_geo";
|
||||
String FRIENDS = "friends";
|
||||
String FAVORITE = "favorite";
|
||||
String UNFAVORITE = "unfavorite";
|
||||
String FOLLOW = "follow";
|
||||
String UNFOLLOW = "unfollow";
|
||||
String USER_LIST_MEMBER_ADDED = "user_list_member_added";
|
||||
String USER_LIST_MEMBER_DELETED = "user_list_member_deleted";
|
||||
String USER_LIST_SUBSCRIBED = "user_list_subscribed";
|
||||
String USER_LIST_UNSUBSCRIBED = "user_list_unsubscribed";
|
||||
String USER_LIST_CREATED = "user_list_created";
|
||||
String USER_LIST_UPDATED = "user_list_updated";
|
||||
String USER_LIST_DESTROYED = "user_list_destroyed";
|
||||
String USER_UPDATE = "user_update";
|
||||
String USER_DELETE = "user_delete";
|
||||
String USER_SUSPEND = "user_suspend";
|
||||
String BLOCK = "block";
|
||||
String UNBLOCK = "unblock";
|
||||
String DISCONNECTION = "disconnection";
|
||||
String UNKNOWN = "unknown";
|
||||
}
|
||||
|
||||
@JsonObject
|
||||
public static class EmptyObject {
|
||||
|
||||
}
|
||||
|
||||
@JsonObject
|
||||
public static class Delete {
|
||||
@JsonField(name = "status")
|
||||
DeletionEvent status;
|
||||
@JsonField(name = "direct_message")
|
||||
DeletionEvent directMessage;
|
||||
|
||||
public DeletionEvent getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public DeletionEvent getDirectMessage() {
|
||||
return directMessage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ import org.mariotaku.restfu.http.HttpResponse;
|
|||
import org.mariotaku.restfu.http.mime.Body;
|
||||
import org.mariotaku.twidere.api.twitter.TwitterException;
|
||||
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
|
||||
import org.mariotaku.twidere.api.twitter.model.CardDataMap;
|
||||
import org.mariotaku.twidere.api.twitter.model.ResponseCode;
|
||||
import org.mariotaku.twidere.api.twitter.model.TwitterResponse;
|
||||
import org.mariotaku.twidere.util.LoganSquareMapperFinder;
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.fragment.support;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore;
|
||||
|
||||
import edu.tsinghua.hotmobi.model.TimelineType;
|
||||
|
||||
public class ActivitiesByFriendsFragment extends CursorActivitiesFragment {
|
||||
|
||||
@Override
|
||||
public boolean getActivities(long[] accountIds, long[] maxIds, long[] sinceIds) {
|
||||
mTwitterWrapper.getActivitiesByFriendsAsync(accountIds, maxIds, sinceIds);
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@TimelineType
|
||||
protected String getTimelineType() {
|
||||
return TimelineType.OTHER;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected String getErrorInfoKey() {
|
||||
return ErrorInfoStore.KEY_ACTIVITIES_BY_FRIENDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getContentUri() {
|
||||
return Activities.ByFriends.CONTENT_URI;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNotificationType() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFilterEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRefreshState() {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ParcelableActivitiesAdapter onCreateAdapter(Context context, boolean compact) {
|
||||
final ParcelableActivitiesAdapter adapter = new ParcelableActivitiesAdapter(context, compact,
|
||||
true);
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments != null) {
|
||||
final Bundle extras = arguments.getBundle(EXTRA_EXTRAS);
|
||||
if (extras != null) {
|
||||
adapter.setFollowingOnly(extras.getBoolean(EXTRA_MY_FOLLOWING_ONLY));
|
||||
adapter.setMentionsOnly(extras.getBoolean(EXTRA_MENTIONS_ONLY));
|
||||
}
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isRefreshing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -87,6 +87,7 @@ import org.mariotaku.twidere.model.ParcelableCredentials;
|
|||
import org.mariotaku.twidere.model.ParcelableDirectMessage;
|
||||
import org.mariotaku.twidere.model.ParcelableUser;
|
||||
import org.mariotaku.twidere.model.ParcelableUserCursorIndices;
|
||||
import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
||||
|
@ -107,7 +108,6 @@ import org.mariotaku.twidere.util.ReadStateManager;
|
|||
import org.mariotaku.twidere.util.UserColorNameManager;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
|
||||
import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
|
||||
import org.mariotaku.twidere.view.ComposeEditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -305,8 +305,11 @@ public class MessagesConversationFragment extends BaseSupportFragment implements
|
|||
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
|
||||
final long userId = args.getLong(EXTRA_RECIPIENT_ID, -1);
|
||||
final int accountPos = accountsSpinnerAdapter.findItemPosition(accountId);
|
||||
account = accountPos < 0 ? DataStoreUtils.getCredentials(activity, accountId)
|
||||
: accountsSpinnerAdapter.getItem(accountPos);
|
||||
if (accountPos >= 0) {
|
||||
mAccountSpinner.setSelection(accountPos);
|
||||
}
|
||||
account = accountPos >= 0 ? accountsSpinnerAdapter.getItem(accountPos) :
|
||||
DataStoreUtils.getCredentials(activity, accountId);
|
||||
recipient = Utils.getUserForConversation(activity, accountId, userId);
|
||||
} else {
|
||||
account = null;
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.mariotaku.twidere.fragment.support;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
|
@ -43,6 +42,8 @@ import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|||
import android.support.v4.content.AsyncTaskLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -432,8 +433,6 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
|
|||
public static class EditUserListDialogFragment extends BaseSupportDialogFragment implements
|
||||
DialogInterface.OnClickListener {
|
||||
|
||||
private MaterialEditText mEditName, mEditDescription;
|
||||
private CheckBox mPublicCheckBox;
|
||||
private String mName, mDescription;
|
||||
private long mAccountId;
|
||||
private long mListId;
|
||||
|
@ -444,14 +443,18 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
|
|||
if (mAccountId <= 0) return;
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE: {
|
||||
mName = ParseUtils.parseString(mEditName.getText());
|
||||
mDescription = ParseUtils.parseString(mEditDescription.getText());
|
||||
mIsPublic = mPublicCheckBox.isChecked();
|
||||
if (mName == null || mName.length() <= 0) return;
|
||||
final AlertDialog alertDialog = (AlertDialog) dialog;
|
||||
final MaterialEditText editName = (MaterialEditText) alertDialog.findViewById(R.id.name);
|
||||
final MaterialEditText editDescription = (MaterialEditText) alertDialog.findViewById(R.id.description);
|
||||
final CheckBox editIsPublic = (CheckBox) alertDialog.findViewById(R.id.is_public);
|
||||
final String name = ParseUtils.parseString(editName.getText());
|
||||
final String description = ParseUtils.parseString(editDescription.getText());
|
||||
final boolean isPublic = editIsPublic.isChecked();
|
||||
if (TextUtils.isEmpty(name)) return;
|
||||
final UserListUpdate update = new UserListUpdate();
|
||||
update.setMode(mIsPublic ? UserList.Mode.PUBLIC : UserList.Mode.PRIVATE);
|
||||
update.setName(mName);
|
||||
update.setDescription(mDescription);
|
||||
update.setMode(isPublic ? UserList.Mode.PUBLIC : UserList.Mode.PRIVATE);
|
||||
update.setName(name);
|
||||
update.setDescription(description);
|
||||
mTwitterWrapper.updateUserListDetails(mAccountId, mListId, update);
|
||||
break;
|
||||
}
|
||||
|
@ -470,23 +473,31 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
|
|||
mIsPublic = bundle == null || bundle.getBoolean(EXTRA_IS_PUBLIC, true);
|
||||
final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity());
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped);
|
||||
final View view = LayoutInflater.from(wrapped).inflate(R.layout.dialog_user_list_detail_editor, null);
|
||||
builder.setView(view);
|
||||
mEditName = (MaterialEditText) view.findViewById(R.id.name);
|
||||
mEditName.addValidator(new UserListNameValidator(getString(R.string.invalid_list_name)));
|
||||
mEditDescription = (MaterialEditText) view.findViewById(R.id.description);
|
||||
mPublicCheckBox = (CheckBox) view.findViewById(R.id.is_public);
|
||||
if (mName != null) {
|
||||
mEditName.setText(mName);
|
||||
}
|
||||
if (mDescription != null) {
|
||||
mEditDescription.setText(mDescription);
|
||||
}
|
||||
mPublicCheckBox.setChecked(mIsPublic);
|
||||
builder.setView(R.layout.dialog_user_list_detail_editor);
|
||||
builder.setTitle(R.string.user_list);
|
||||
builder.setPositiveButton(android.R.string.ok, this);
|
||||
builder.setNegativeButton(android.R.string.cancel, this);
|
||||
return builder.create();
|
||||
final AlertDialog dialog = builder.create();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
|
||||
AlertDialog alertDialog = (AlertDialog) dialog;
|
||||
MaterialEditText mEditName = (MaterialEditText) alertDialog.findViewById(R.id.name);
|
||||
MaterialEditText mEditDescription = (MaterialEditText) alertDialog.findViewById(R.id.description);
|
||||
CheckBox mPublicCheckBox = (CheckBox) alertDialog.findViewById(R.id.is_public);
|
||||
|
||||
mEditName.addValidator(new UserListNameValidator(getString(R.string.invalid_list_name)));
|
||||
if (mName != null) {
|
||||
mEditName.setText(mName);
|
||||
}
|
||||
if (mDescription != null) {
|
||||
mEditDescription.setText(mDescription);
|
||||
}
|
||||
mPublicCheckBox.setChecked(mIsPublic);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.mariotaku.twidere.Constants;
|
|||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.util.MenuUtils;
|
||||
|
||||
import static org.mariotaku.twidere.util.MenuUtils.addIntentToMenu;
|
||||
import static org.mariotaku.twidere.util.Utils.createStatusShareIntent;
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,10 +2,6 @@ package org.mariotaku.twidere.model;
|
|||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
|
||||
|
||||
import org.mariotaku.library.objectcursor.annotation.CursorField;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/25.
|
||||
|
|
|
@ -61,7 +61,7 @@ public class FavoriteTaskEvent {
|
|||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(ParcelableStatus status) {
|
||||
public void setStatus(@Nullable ParcelableStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.mariotaku.twidere.service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
|
@ -27,6 +28,7 @@ import android.os.Build;
|
|||
/**
|
||||
* Created by mariotaku on 14/12/12.
|
||||
*/
|
||||
@SuppressLint("Registered")
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class BackgroundJobService extends JobService {
|
||||
@Override
|
||||
|
|
|
@ -28,9 +28,9 @@ import org.mariotaku.twidere.activity.SettingsActivity;
|
|||
import org.mariotaku.twidere.api.twitter.TwitterException;
|
||||
import org.mariotaku.twidere.api.twitter.TwitterUserStream;
|
||||
import org.mariotaku.twidere.api.twitter.UserStreamCallback;
|
||||
import org.mariotaku.twidere.api.twitter.model.DeletionEvent;
|
||||
import org.mariotaku.twidere.api.twitter.model.DirectMessage;
|
||||
import org.mariotaku.twidere.api.twitter.model.Status;
|
||||
import org.mariotaku.twidere.api.twitter.model.StatusDeletionNotice;
|
||||
import org.mariotaku.twidere.api.twitter.model.User;
|
||||
import org.mariotaku.twidere.api.twitter.model.UserList;
|
||||
import org.mariotaku.twidere.api.twitter.model.Warning;
|
||||
|
@ -221,16 +221,16 @@ public class StreamingService extends Service implements Constants {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDeletionNotice(final long directMessageId, final long userId) {
|
||||
final String where = DirectMessages.MESSAGE_ID + " = " + directMessageId;
|
||||
public void onDirectMessageDeleted(final DeletionEvent event) {
|
||||
final String where = Expression.equals(DirectMessages.MESSAGE_ID, event.getId()).getSQL();
|
||||
for (final Uri uri : MESSAGES_URIS) {
|
||||
resolver.delete(uri, where, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeletionNotice(final StatusDeletionNotice statusDeletionNotice) {
|
||||
final long statusId = statusDeletionNotice.getStatusId();
|
||||
public void onStatusDeleted(final DeletionEvent event) {
|
||||
final long statusId = event.getId();
|
||||
resolver.delete(Statuses.CONTENT_URI, Expression.equals(Statuses.STATUS_ID, statusId).getSQL(), null);
|
||||
resolver.delete(Activities.AboutMe.CONTENT_URI, Expression.equals(Activities.AboutMe.STATUS_ID, statusId).getSQL(), null);
|
||||
}
|
||||
|
@ -298,9 +298,9 @@ public class StreamingService extends Service implements Constants {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onFavorite(final User source, final User target, final Status favoritedStatus) {
|
||||
public void onFavorite(final User source, final User target, final Status targetStatus) {
|
||||
final String message = String.format("%s favorited %s's tweet: %s", source.getScreenName(),
|
||||
target.getScreenName(), favoritedStatus.getText());
|
||||
target.getScreenName(), targetStatus.getText());
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
|
@ -362,9 +362,9 @@ public class StreamingService extends Service implements Constants {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onUnfavorite(final User source, final User target, final Status unfavoritedStatus) {
|
||||
public void onUnfavorite(final User source, final User target, final Status targetStatus) {
|
||||
final String message = String.format("%s unfavorited %s's tweet: %s", source.getScreenName(),
|
||||
target.getScreenName(), unfavoritedStatus.getText());
|
||||
target.getScreenName(), targetStatus.getText());
|
||||
Log.d(LOGTAG, message);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.support.annotation.MainThread;
|
|||
import android.support.annotation.WorkerThread;
|
||||
|
||||
/**
|
||||
* Abstract Task class can be used with different implementations
|
||||
* Created by mariotaku on 16/2/24.
|
||||
*/
|
||||
public abstract class AbstractTask<Params, Result, Callback> {
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.os.AsyncTask;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.text;
|
||||
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.LineHeightSpan;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 14/11/22.
|
||||
*/
|
||||
public class ParagraphSpacingSpan implements LineHeightSpan {
|
||||
|
||||
private final float spacingMultiplier;
|
||||
|
||||
public ParagraphSpacingSpan(float spacingMultiplier) {
|
||||
this.spacingMultiplier = spacingMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chooseHeight(CharSequence text, int start, int end,
|
||||
int spanstartv, int v, FontMetricsInt fm) {
|
||||
Spanned spanned = (Spanned) text;
|
||||
int en = spanned.getSpanEnd(this);
|
||||
if (end - 1 == en) {
|
||||
final int extra = Math.round((fm.bottom - fm.top) * (spacingMultiplier - 1));
|
||||
fm.descent += extra;
|
||||
fm.bottom += extra;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -99,11 +99,6 @@ public class CustomTabUtils implements Constants {
|
|||
UserListTimelineFragment.class, R.string.list_timeline, R.drawable.ic_action_list,
|
||||
CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_USER_LIST, 7));
|
||||
|
||||
// CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.ACTIVITIES_BY_FRIENDS, new CustomTabConfiguration(
|
||||
// ActivitiesByFriendsFragment.class, R.string.activities_by_friends,
|
||||
// R.drawable.ic_action_accounts, CustomTabConfiguration.ACCOUNT_OPTIONAL,
|
||||
// CustomTabConfiguration.FIELD_TYPE_NONE, 9));
|
||||
|
||||
CUSTOM_TABS_CONFIGURATION_MAP.put(CustomTabType.RETWEETS_OF_ME, new CustomTabConfiguration(
|
||||
RetweetsOfMeFragment.class, R.string.retweets_of_me, R.drawable.ic_action_retweet,
|
||||
CustomTabConfiguration.ACCOUNT_REQUIRED, CustomTabConfiguration.FIELD_TYPE_NONE, 10));
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.util;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.mariotaku.sqliteqb.library.SQLFunctions;
|
||||
|
||||
public class DatabaseQueryUtils {
|
||||
|
||||
public static int count(final SQLiteDatabase db, final String table, final String selection,
|
||||
final String[] selectionArgs, final String groupBy, final String having, final String orderBy) {
|
||||
if (db == null) return -1;
|
||||
final Cursor c = db.query(table, new String[]{SQLFunctions.COUNT()}, selection, selectionArgs, groupBy, having, orderBy);
|
||||
try {
|
||||
if (c.moveToFirst()) return c.getInt(0);
|
||||
return -1;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static int count(final SQLiteDatabase db, boolean distinct, String table, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
|
||||
if (db == null) return -1;
|
||||
final Cursor c = db.query(distinct, table, new String[]{SQLFunctions.COUNT()}, selection, selectionArgs, groupBy, having, orderBy, limit);
|
||||
try {
|
||||
if (c.moveToFirst()) return c.getInt(0);
|
||||
return -1;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static int count(final SQLiteDatabase db, String table, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
|
||||
if (db == null) return -1;
|
||||
final Cursor c = db.query(table, new String[]{SQLFunctions.COUNT()}, selection, selectionArgs, groupBy, having, orderBy, limit);
|
||||
try {
|
||||
if (c.moveToFirst()) return c.getInt(0);
|
||||
return -1;
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -23,7 +23,6 @@ import android.annotation.SuppressLint;
|
|||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -69,17 +68,13 @@ import android.support.v4.app.ActivityCompat;
|
|||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v4.view.ActionProvider;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.ShareActionProvider;
|
||||
import android.system.ErrnoException;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
|
@ -121,8 +116,6 @@ import org.mariotaku.twidere.BuildConfig;
|
|||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.activity.CopyLinkActivity;
|
||||
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
|
||||
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
|
||||
import org.mariotaku.twidere.adapter.iface.IBaseAdapter;
|
||||
import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter;
|
||||
import org.mariotaku.twidere.annotation.CustomTabType;
|
||||
|
@ -136,10 +129,7 @@ import org.mariotaku.twidere.api.twitter.model.Relationship;
|
|||
import org.mariotaku.twidere.api.twitter.model.Status;
|
||||
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
|
||||
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
|
||||
import org.mariotaku.twidere.fragment.support.AbsStatusesFragment.DefaultOnLikedListener;
|
||||
import org.mariotaku.twidere.fragment.support.AccountsManagerFragment;
|
||||
import org.mariotaku.twidere.fragment.support.AddStatusFilterDialogFragment;
|
||||
import org.mariotaku.twidere.fragment.support.DestroyStatusDialogFragment;
|
||||
import org.mariotaku.twidere.fragment.support.DirectMessagesFragment;
|
||||
import org.mariotaku.twidere.fragment.support.DraftsFragment;
|
||||
import org.mariotaku.twidere.fragment.support.FiltersFragment;
|
||||
|
@ -151,7 +141,6 @@ import org.mariotaku.twidere.fragment.support.MutesUsersListFragment;
|
|||
import org.mariotaku.twidere.fragment.support.SavedSearchesListFragment;
|
||||
import org.mariotaku.twidere.fragment.support.ScheduledStatusesFragment;
|
||||
import org.mariotaku.twidere.fragment.support.SearchFragment;
|
||||
import org.mariotaku.twidere.fragment.support.SetUserNicknameDialogFragment;
|
||||
import org.mariotaku.twidere.fragment.support.StatusFavoritersListFragment;
|
||||
import org.mariotaku.twidere.fragment.support.StatusFragment;
|
||||
import org.mariotaku.twidere.fragment.support.StatusRepliesListFragment;
|
||||
|
@ -172,10 +161,7 @@ import org.mariotaku.twidere.fragment.support.UserMentionsFragment;
|
|||
import org.mariotaku.twidere.fragment.support.UserProfileEditorFragment;
|
||||
import org.mariotaku.twidere.fragment.support.UserTimelineFragment;
|
||||
import org.mariotaku.twidere.fragment.support.UsersListFragment;
|
||||
import org.mariotaku.twidere.graphic.ActionIconDrawable;
|
||||
import org.mariotaku.twidere.graphic.PaddingDrawable;
|
||||
import org.mariotaku.twidere.menu.SupportStatusShareProvider;
|
||||
import org.mariotaku.twidere.menu.support.FavoriteItemProvider;
|
||||
import org.mariotaku.twidere.model.AccountPreferences;
|
||||
import org.mariotaku.twidere.model.ParcelableAccount;
|
||||
import org.mariotaku.twidere.model.ParcelableCredentials;
|
||||
|
@ -201,7 +187,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Conversati
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||
import org.mariotaku.twidere.service.RefreshService;
|
||||
import org.mariotaku.twidere.util.TwidereLinkify.HighlightStyle;
|
||||
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
|
||||
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
|
||||
import org.mariotaku.twidere.view.ShapedImageView;
|
||||
import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle;
|
||||
|
@ -466,6 +451,7 @@ public final class Utils implements Constants {
|
|||
if (extras != null) {
|
||||
args.putAll(extras);
|
||||
}
|
||||
boolean isAccountIdRequired = true;
|
||||
switch (linkId) {
|
||||
case LINK_ID_ACCOUNTS: {
|
||||
fragment = new AccountsManagerFragment();
|
||||
|
@ -609,6 +595,7 @@ public final class Utils implements Constants {
|
|||
}
|
||||
case LINK_ID_DIRECT_MESSAGES_CONVERSATION: {
|
||||
fragment = new MessagesConversationFragment();
|
||||
isAccountIdRequired = false;
|
||||
final String paramRecipientId = uri.getQueryParameter(QUERY_PARAM_RECIPIENT_ID);
|
||||
final String paramScreenName = uri.getQueryParameter(QUERY_PARAM_SCREEN_NAME);
|
||||
final long conversationId = NumberUtils.toLong(paramRecipientId, -1);
|
||||
|
@ -775,7 +762,7 @@ public final class Utils implements Constants {
|
|||
final String paramAccountName = uri.getQueryParameter(QUERY_PARAM_ACCOUNT_NAME);
|
||||
if (paramAccountName != null) {
|
||||
args.putLong(EXTRA_ACCOUNT_ID, DataStoreUtils.getAccountId(context, paramAccountName));
|
||||
} else {
|
||||
} else if (isAccountIdRequired) {
|
||||
final long accountId = getDefaultAccountId(context);
|
||||
if (isMyAccount(context, accountId)) {
|
||||
args.putLong(EXTRA_ACCOUNT_ID, accountId);
|
||||
|
@ -1695,9 +1682,11 @@ public final class Utils implements Constants {
|
|||
final Uri.Builder builder = new Uri.Builder();
|
||||
builder.scheme(SCHEME_TWIDERE);
|
||||
builder.authority(AUTHORITY_DIRECT_MESSAGES_CONVERSATION);
|
||||
if (accountId > 0 && recipientId > 0) {
|
||||
if (accountId > 0) {
|
||||
builder.appendQueryParameter(QUERY_PARAM_ACCOUNT_ID, String.valueOf(accountId));
|
||||
builder.appendQueryParameter(QUERY_PARAM_RECIPIENT_ID, String.valueOf(recipientId));
|
||||
if (recipientId > 0) {
|
||||
builder.appendQueryParameter(QUERY_PARAM_RECIPIENT_ID, String.valueOf(recipientId));
|
||||
}
|
||||
}
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
|
||||
intent.setPackage(BuildConfig.APPLICATION_ID);
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.os.Parcel;
|
|||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Backward compatibility utilities for {@link Activity}
|
||||
* Created by mariotaku on 14/11/4.
|
||||
*/
|
||||
public class ActivitySupport {
|
||||
|
|
|
@ -361,7 +361,7 @@ public class HeaderDrawerLayout extends ViewGroup {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onViewReleased(View releasedChild, float xvel, float yvel) {
|
||||
public void onViewReleased(View releasedChild, float xVel, float yVel) {
|
||||
mDrawer.mDragHelper.flingCapturedView(mDrawer.getPaddingLeft(),
|
||||
mDrawer.getHeaderTopMinimum(), mDrawer.getPaddingLeft(),
|
||||
mDrawer.getHeaderTopMaximum());
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.mariotaku.twidere.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.mariotaku.twidere.view;
|
|||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
public class SquareActionIconButton extends ActionIconButton {
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ import com.pnikosis.materialishprogress.ProgressWheel;
|
|||
import org.mariotaku.twidere.view.iface.IThemeAccentView;
|
||||
|
||||
/**
|
||||
* ProgressWheel view that supports theme color settings
|
||||
*
|
||||
* Created by mariotaku on 15/4/25.
|
||||
*/
|
||||
public class AccentProgressWheel extends ProgressWheel implements IThemeAccentView {
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
package org.mariotaku.twidere.view.themed;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
|
|
|
@ -66,7 +66,8 @@
|
|||
android:contentDescription="@string/profile_image"
|
||||
android:scaleType="centerCrop"
|
||||
app:sivBorder="true"
|
||||
app:sivBorderWidth="1dp"/>
|
||||
app:sivBorderWidth="1dp"
|
||||
tools:src="@mipmap/ic_launcher"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/profile_type"
|
||||
|
@ -131,7 +132,7 @@
|
|||
android:layout_marginTop="@dimen/element_spacing_small"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
tools:text="Jan 1 2015 0:00, via Twidere"/>
|
||||
tools:text="Jan 1 2015 0:00 · Twidere"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -376,7 +377,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/quote_original_link"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
tools:visibility="visible">
|
||||
|
||||
<include layout="@layout/adapter_item_status_count_label"/>
|
||||
|
||||
|
@ -391,7 +393,8 @@
|
|||
android:layout_alignTop="@+id/counts_users_height_holder"
|
||||
android:layout_marginLeft="@dimen/element_spacing_small"
|
||||
android:layout_marginRight="@dimen/element_spacing_small"
|
||||
android:splitMotionEvents="false"/>
|
||||
android:splitMotionEvents="false"
|
||||
tools:visibility="invisible"/>
|
||||
|
||||
<android.support.v7.widget.ActionMenuView
|
||||
android:id="@+id/menu_bar"
|
||||
|
|
Loading…
Reference in New Issue