removed unused code

bug fixes
This commit is contained in:
Mariotaku Lee 2016-02-26 16:11:53 +08:00
parent 56f77bd947
commit 1e956016f5
56 changed files with 462 additions and 1329 deletions

View File

@ -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

View File

@ -108,6 +108,7 @@ public class TwitterException extends Exception implements TwitterResponse, Http
/**
* {@inheritDoc}
*/
@AccessLevel
@Override
public int getAccessLevel() {
return InternalParseUtil.toAccessLevel(httpResponse);

View File

@ -42,6 +42,7 @@ public class PageableResponseList<T> extends ArrayList<T> implements TwitterResp
}
@Override
@AccessLevel
public final int getAccessLevel() {
return accessLevel;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 {
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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": {

View File

@ -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 {

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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"/>

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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')

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;
/**

View File

@ -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.

View File

@ -61,7 +61,7 @@ public class FavoriteTaskEvent {
return status;
}
public void setStatus(ParcelableStatus status) {
public void setStatus(@Nullable ParcelableStatus status) {
this.status = status;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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> {

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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));

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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());

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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"