code cleanup
This commit is contained in:
parent
a4f3fd94f8
commit
8cddbb0dfc
|
@ -10,6 +10,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/11.
|
* Created by mariotaku on 16/3/11.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface BlocksResources {
|
public interface BlocksResources {
|
||||||
|
|
||||||
@POST("/blocks/create.json")
|
@POST("/blocks/create.json")
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.mariotaku.microblog.library.twitter.model.DirectMessage;
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/31.
|
* Created by mariotaku on 16/3/31.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface DirectMessagesResources {
|
public interface DirectMessagesResources {
|
||||||
|
|
||||||
@POST("/direct_messages/new.json")
|
@POST("/direct_messages/new.json")
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.mariotaku.microblog.library.twitter.model.Status;
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/11.
|
* Created by mariotaku on 16/3/11.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface FavoritesResources {
|
public interface FavoritesResources {
|
||||||
|
|
||||||
@POST("/favorites/create/{id}.json")
|
@POST("/favorites/create/{id}.json")
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/11.
|
* Created by mariotaku on 16/3/11.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface FriendshipsResources {
|
public interface FriendshipsResources {
|
||||||
|
|
||||||
@POST("/friendships/create.json")
|
@POST("/friendships/create.json")
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/4.
|
* Created by mariotaku on 16/3/4.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface GroupResources {
|
public interface GroupResources {
|
||||||
|
|
||||||
@GET("/statusnet/groups/timeline/{group_id}.json")
|
@GET("/statusnet/groups/timeline/{group_id}.json")
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.mariotaku.restfu.annotation.param.Param;
|
||||||
import org.mariotaku.restfu.annotation.param.Queries;
|
import org.mariotaku.restfu.annotation.param.Queries;
|
||||||
import org.mariotaku.restfu.annotation.param.Query;
|
import org.mariotaku.restfu.annotation.param.Query;
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
public interface ListResources {
|
public interface ListResources {
|
||||||
@POST("/lists/members/create.json")
|
@POST("/lists/members/create.json")
|
||||||
UserList addUserListMember(@Query("list_id") String listId, @Query("user_id") String userId) throws MicroBlogException;
|
UserList addUserListMember(@Query("list_id") String listId, @Query("user_id") String userId) throws MicroBlogException;
|
||||||
|
|
|
@ -29,8 +29,10 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Mapper for IDs object
|
||||||
* Created by mariotaku on 15/10/21.
|
* Created by mariotaku on 15/10/21.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class IDs$$JsonObjectMapper extends JsonMapper<IDs> {
|
public class IDs$$JsonObjectMapper extends JsonMapper<IDs> {
|
||||||
|
|
||||||
@SuppressWarnings("TryWithIdenticalCatches")
|
@SuppressWarnings("TryWithIdenticalCatches")
|
||||||
|
|
|
@ -27,9 +27,9 @@ import org.mariotaku.twidere.util.model.AccountDetailsUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Object holding account info and credentials
|
||||||
* Created by mariotaku on 2016/12/3.
|
* Created by mariotaku on 2016/12/3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ParcelablePlease
|
@ParcelablePlease
|
||||||
@JsonObject
|
@JsonObject
|
||||||
public class AccountDetails implements Parcelable, Comparable<AccountDetails> {
|
public class AccountDetails implements Parcelable, Comparable<AccountDetails> {
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -445,11 +444,11 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnJsonParseComplete
|
@OnJsonParseComplete
|
||||||
void onParseComplete() throws IOException {
|
void onParseComplete() {
|
||||||
fixSortId();
|
fixSortId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void fixSortId() {
|
private void fixSortId() {
|
||||||
if (sort_id <= 0) {
|
if (sort_id <= 0) {
|
||||||
try {
|
try {
|
||||||
sort_id = Long.parseLong(id);
|
sort_id = Long.parseLong(id);
|
||||||
|
|
|
@ -1,53 +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.model.util;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.mariotaku.library.objectcursor.converter.CursorFieldConverter;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/11/27.
|
|
||||||
*/
|
|
||||||
public class JSONObjectConverter implements CursorFieldConverter<JSONObject> {
|
|
||||||
@Override
|
|
||||||
public JSONObject parseField(Cursor cursor, int columnIndex, ParameterizedType fieldType) {
|
|
||||||
final String string = cursor.getString(columnIndex);
|
|
||||||
if (TextUtils.isEmpty(string)) return null;
|
|
||||||
try {
|
|
||||||
return new JSONObject(string);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeField(ContentValues values, JSONObject object, String columnName, ParameterizedType fieldType) {
|
|
||||||
if (object != null) {
|
|
||||||
values.put(columnName, object.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +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.model.util;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
|
|
||||||
import com.hannesdorfmann.parcelableplease.ParcelBagger;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/11/28.
|
|
||||||
*/
|
|
||||||
public class JSONParcelBagger implements ParcelBagger<JSONObject> {
|
|
||||||
@Override
|
|
||||||
public void write(JSONObject value, Parcel out, int flags) {
|
|
||||||
if (value != null) {
|
|
||||||
out.writeString(value.toString());
|
|
||||||
} else {
|
|
||||||
out.writeString(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject read(Parcel in) {
|
|
||||||
final String s = in.readString();
|
|
||||||
if (s != null) {
|
|
||||||
try {
|
|
||||||
return new JSONObject(s);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +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.util;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class HtmlLinkExtractor {
|
|
||||||
|
|
||||||
private final Pattern patternTag, patternLink;
|
|
||||||
|
|
||||||
private static final String HTML_A_TAG_PATTERN = "(?i)<a([^>]+)>(.+?)</a>";
|
|
||||||
private static final String HTML_A_HREF_TAG_PATTERN = "\\s*(?i)href\\s*=\\s*(\"([^\"]*\")|'[^']*'|([^'\">\\s]+))";
|
|
||||||
|
|
||||||
public HtmlLinkExtractor() {
|
|
||||||
patternTag = Pattern.compile(HTML_A_TAG_PATTERN);
|
|
||||||
patternLink = Pattern.compile(HTML_A_HREF_TAG_PATTERN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate html with regular expression
|
|
||||||
*
|
|
||||||
* @param html html content for validation
|
|
||||||
* @return Vector links and link text
|
|
||||||
*/
|
|
||||||
public Vector<HtmlLink> grabLinks(final String html) {
|
|
||||||
final Vector<HtmlLink> result = new Vector<>();
|
|
||||||
final Matcher matcherTag = patternTag.matcher(html);
|
|
||||||
while (matcherTag.find()) {
|
|
||||||
final String href = matcherTag.group(1); // href
|
|
||||||
final String linkText = matcherTag.group(2); // link text
|
|
||||||
final Matcher matcherLink = patternLink.matcher(href);
|
|
||||||
while (matcherLink.find()) {
|
|
||||||
final String link = matcherLink.group(1); // link
|
|
||||||
final HtmlLink obj = new HtmlLink(link, linkText);
|
|
||||||
result.add(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HtmlLink {
|
|
||||||
|
|
||||||
private final String link;
|
|
||||||
private final String text;
|
|
||||||
|
|
||||||
private HtmlLink(final String link, final String text) {
|
|
||||||
this.link = replaceInvalidChar(link);
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLink() {
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLinkText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String replaceInvalidChar(String link) {
|
|
||||||
link = link.replaceAll("'", "");
|
|
||||||
link = link.replaceAll("\"", "");
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +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.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/11/25.
|
|
||||||
*/
|
|
||||||
public class Nullables {
|
|
||||||
|
|
||||||
private Nullables() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static <T> List<T> list(@Nullable List<T> list) {
|
|
||||||
if (list == null) return Collections.emptyList();
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static <T> T assertNonNull(@Nullable T object) {
|
|
||||||
if (object == null) throw new NullPointerException();
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,10 +29,6 @@ public final class ParseUtils implements TwidereConstants {
|
||||||
private ParseUtils() {
|
private ParseUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseString(final String object) {
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String parseString(final Object object) {
|
public static String parseString(final Object object) {
|
||||||
return parseString(object, null);
|
return parseString(object, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,5 +35,6 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile "com.android.support:support-compat:$android_support_lib_version"
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import android.graphics.Shader.TileMode;
|
||||||
import android.graphics.drawable.AnimationDrawable;
|
import android.graphics.drawable.AnimationDrawable;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -77,9 +78,9 @@ public class NyanDrawingHelper {
|
||||||
} else {
|
} else {
|
||||||
sakamotoRes = R.drawable.nyan_sakamoto;
|
sakamotoRes = R.drawable.nyan_sakamoto;
|
||||||
}
|
}
|
||||||
mSakamotoHelper = new DrawableDrawingHelper(mResources.getDrawable(sakamotoRes));
|
mSakamotoHelper = new DrawableDrawingHelper(ContextCompat.getDrawable(context, sakamotoRes));
|
||||||
mDrawingHelpers = new IDrawingHelper[]{mStarsHelper, mRainbowHelper, mSakamotoHelper};
|
mDrawingHelpers = new IDrawingHelper[]{mStarsHelper, mRainbowHelper, mSakamotoHelper};
|
||||||
mBackgroundColor = mResources.getColor(R.color.nyan_background);
|
mBackgroundColor = ContextCompat.getColor(context, R.color.nyan_background);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void dispatchDraw(final Canvas canvas) {
|
public final void dispatchDraw(final Canvas canvas) {
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
package org.mariotaku.twidere.util
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 16/1/23.
|
|
||||||
*/
|
|
||||||
class TwidereMathUtilsTest {
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testClamp() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testClamp1() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testNextPowerOf2() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testPrevPowerOf2() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testSum() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testSum1() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testSum2() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testInRange() {
|
|
||||||
assertTrue(TwidereMathUtils.inRange(5, 0, 10, TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(0, 0, 10, TwidereMathUtils.RANGE_EXCLUSIVE_EXCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(0, 5, 10, TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(5, 5, 10, TwidereMathUtils.RANGE_EXCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(10, 5, 10, TwidereMathUtils.RANGE_INCLUSIVE_EXCLUSIVE))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testInRange1() {
|
|
||||||
assertTrue(TwidereMathUtils.inRange(5f, 0f, 10f, TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(0f, 0f, 10f, TwidereMathUtils.RANGE_EXCLUSIVE_EXCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(0f, 5f, 10f, TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(5f, 5f, 10f, TwidereMathUtils.RANGE_EXCLUSIVE_INCLUSIVE))
|
|
||||||
assertFalse(TwidereMathUtils.inRange(10f, 5f, 10f, TwidereMathUtils.RANGE_INCLUSIVE_EXCLUSIVE))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
TwitterCardViewFactoryImpl
|
|
|
@ -56,6 +56,7 @@ public final class CRLFLineReader extends BufferedReader
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
int intch;
|
int intch;
|
||||||
boolean prevWasCR = false;
|
boolean prevWasCR = false;
|
||||||
|
//noinspection SynchronizeOnNonFinalField
|
||||||
synchronized(lock) { // make thread-safe (hopefully!)
|
synchronized(lock) { // make thread-safe (hopefully!)
|
||||||
while((intch = read()) != -1)
|
while((intch = read()) != -1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,135 +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.microblog.library.twitter.util;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.TreeNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Dan Checkoway - dcheckoway at gmail.com
|
|
||||||
* @since Twitter4J 2.1.9
|
|
||||||
*/
|
|
||||||
public final class JSONObjectType {
|
|
||||||
private JSONObjectType() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
SENDER,
|
|
||||||
STATUS,
|
|
||||||
DIRECT_MESSAGE,
|
|
||||||
DELETE,
|
|
||||||
LIMIT,
|
|
||||||
STALL_WARNING,
|
|
||||||
SCRUB_GEO,
|
|
||||||
FRIENDS,
|
|
||||||
FAVORITE,
|
|
||||||
UNFAVORITE,
|
|
||||||
FOLLOW,
|
|
||||||
UNFOLLOW,
|
|
||||||
USER_LIST_MEMBER_ADDED,
|
|
||||||
USER_LIST_MEMBER_DELETED,
|
|
||||||
USER_LIST_SUBSCRIBED,
|
|
||||||
USER_LIST_UNSUBSCRIBED,
|
|
||||||
USER_LIST_CREATED,
|
|
||||||
USER_LIST_UPDATED,
|
|
||||||
USER_LIST_DESTROYED,
|
|
||||||
USER_UPDATE,
|
|
||||||
USER_DELETE,
|
|
||||||
USER_SUSPEND,
|
|
||||||
BLOCK,
|
|
||||||
UNBLOCK,
|
|
||||||
DISCONNECTION,
|
|
||||||
UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the respective object type for a given JSONObject. This
|
|
||||||
* method inspects the object to figure out what type of object it
|
|
||||||
* represents. This is useful when processing JSON events of mixed type
|
|
||||||
* from a stream, in which case you may need to know what type of object
|
|
||||||
* to construct, or how to handle the event properly.
|
|
||||||
*
|
|
||||||
* @param json the JSONObject whose type should be determined
|
|
||||||
* @return the determined JSONObjectType, or null if not recognized
|
|
||||||
*/
|
|
||||||
public static Type determine(TreeNode json) {
|
|
||||||
// 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 (json.get("sender") != null) {
|
|
||||||
return Type.SENDER;
|
|
||||||
} else if (json.get("text") != null) {
|
|
||||||
return Type.STATUS;
|
|
||||||
} else if (json.get("direct_message") != null) {
|
|
||||||
return Type.DIRECT_MESSAGE;
|
|
||||||
} else if (json.get("delete") != null) {
|
|
||||||
return Type.DELETE;
|
|
||||||
} else if (json.get("limit") != null) {
|
|
||||||
return Type.LIMIT;
|
|
||||||
} else if (json.get("warning") != null) {
|
|
||||||
return Type.STALL_WARNING;
|
|
||||||
} else if (json.get("scrub_geo") != null) {
|
|
||||||
return Type.SCRUB_GEO;
|
|
||||||
} else if (json.get("friends") != null) {
|
|
||||||
return Type.FRIENDS;
|
|
||||||
} else if (json.get("event") != null) {
|
|
||||||
String event;
|
|
||||||
event = json.get("event").asToken().asString();
|
|
||||||
if ("favorite".equals(event)) {
|
|
||||||
return Type.FAVORITE;
|
|
||||||
} else if ("unfavorite".equals(event)) {
|
|
||||||
return Type.UNFAVORITE;
|
|
||||||
} else if ("follow".equals(event)) {
|
|
||||||
return Type.FOLLOW;
|
|
||||||
} else if ("unfollow".equals(event)) {
|
|
||||||
return Type.UNFOLLOW;
|
|
||||||
} else if (event.startsWith("list")) {
|
|
||||||
if ("list_member_added".equals(event)) {
|
|
||||||
return Type.USER_LIST_MEMBER_ADDED;
|
|
||||||
} else if ("list_member_removed".equals(event)) {
|
|
||||||
return Type.USER_LIST_MEMBER_DELETED;
|
|
||||||
} else if ("list_user_subscribed".equals(event)) {
|
|
||||||
return Type.USER_LIST_SUBSCRIBED;
|
|
||||||
} else if ("list_user_unsubscribed".equals(event)) {
|
|
||||||
return Type.USER_LIST_UNSUBSCRIBED;
|
|
||||||
} else if ("list_created".equals(event)) {
|
|
||||||
return Type.USER_LIST_CREATED;
|
|
||||||
} else if ("list_updated".equals(event)) {
|
|
||||||
return Type.USER_LIST_UPDATED;
|
|
||||||
} else if ("list_destroyed".equals(event)) {
|
|
||||||
return Type.USER_LIST_DESTROYED;
|
|
||||||
}
|
|
||||||
} else if ("user_update".equals(event)) {
|
|
||||||
return Type.USER_UPDATE;
|
|
||||||
} else if ("user_delete".equals(event)) {
|
|
||||||
return Type.USER_DELETE;
|
|
||||||
} else if ("user_suspend".equals(event)) {
|
|
||||||
return Type.USER_SUSPEND;
|
|
||||||
} else if ("block".equals(event)) {
|
|
||||||
return Type.BLOCK;
|
|
||||||
} else if ("unblock".equals(event)) {
|
|
||||||
return Type.UNBLOCK;
|
|
||||||
}
|
|
||||||
} else if (json.get("disconnect") != null) {
|
|
||||||
return Type.DISCONNECTION;
|
|
||||||
}
|
|
||||||
return Type.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -51,7 +51,6 @@ import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_TITLE;
|
||||||
public final class DataExportImportTypeSelectorDialogFragment extends BaseDialogFragment implements
|
public final class DataExportImportTypeSelectorDialogFragment extends BaseDialogFragment implements
|
||||||
OnMultiChoiceClickListener, OnClickListener, OnShowListener, OnItemClickListener {
|
OnMultiChoiceClickListener, OnClickListener, OnShowListener, OnItemClickListener {
|
||||||
|
|
||||||
private TypeAdapter mAdapter;
|
|
||||||
private ListView mListView;
|
private ListView mListView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -84,20 +83,20 @@ public final class DataExportImportTypeSelectorDialogFragment extends BaseDialog
|
||||||
public final Dialog onCreateDialog(final Bundle savedInstanceState) {
|
public final Dialog onCreateDialog(final Bundle savedInstanceState) {
|
||||||
final Context context = getActivity();
|
final Context context = getActivity();
|
||||||
final int flags = getEnabledFlags();
|
final int flags = getEnabledFlags();
|
||||||
mAdapter = new TypeAdapter(context, flags);
|
|
||||||
mListView = new ListView(context);
|
mListView = new ListView(context);
|
||||||
mAdapter.add(new Type(R.string.settings, DataImportExportUtils.FLAG_PREFERENCES));
|
final TypeAdapter adapter = new TypeAdapter(context, flags);
|
||||||
mAdapter.add(new Type(R.string.title_nicknames, DataImportExportUtils.FLAG_NICKNAMES));
|
adapter.add(new Type(R.string.settings, DataImportExportUtils.FLAG_PREFERENCES));
|
||||||
mAdapter.add(new Type(R.string.title_user_colors, DataImportExportUtils.FLAG_USER_COLORS));
|
adapter.add(new Type(R.string.title_nicknames, DataImportExportUtils.FLAG_NICKNAMES));
|
||||||
mAdapter.add(new Type(R.string.custom_host_mapping, DataImportExportUtils.FLAG_HOST_MAPPING));
|
adapter.add(new Type(R.string.title_user_colors, DataImportExportUtils.FLAG_USER_COLORS));
|
||||||
mAdapter.add(new Type(R.string.keyboard_shortcuts, DataImportExportUtils.FLAG_KEYBOARD_SHORTCUTS));
|
adapter.add(new Type(R.string.custom_host_mapping, DataImportExportUtils.FLAG_HOST_MAPPING));
|
||||||
mAdapter.add(new Type(R.string.title_filters, DataImportExportUtils.FLAG_FILTERS));
|
adapter.add(new Type(R.string.keyboard_shortcuts, DataImportExportUtils.FLAG_KEYBOARD_SHORTCUTS));
|
||||||
mAdapter.add(new Type(R.string.tabs, DataImportExportUtils.FLAG_TABS));
|
adapter.add(new Type(R.string.title_filters, DataImportExportUtils.FLAG_FILTERS));
|
||||||
mListView.setAdapter(mAdapter);
|
adapter.add(new Type(R.string.tabs, DataImportExportUtils.FLAG_TABS));
|
||||||
|
mListView.setAdapter(adapter);
|
||||||
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
mListView.setOnItemClickListener(this);
|
mListView.setOnItemClickListener(this);
|
||||||
for (int i = 0, j = mAdapter.getCount(); i < j; i++) {
|
for (int i = 0, j = adapter.getCount(); i < j; i++) {
|
||||||
mListView.setItemChecked(i, mAdapter.isEnabled(i));
|
mListView.setItemChecked(i, adapter.isEnabled(i));
|
||||||
}
|
}
|
||||||
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||||
builder.setTitle(getTitle());
|
builder.setTitle(getTitle());
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2010 Daniel Nilsson
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mariotaku.twidere.graphic;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.ColorFilter;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This drawable that draws a simple white and gray chessboard pattern. It's
|
|
||||||
* pattern you will often see as a background behind a partly transparent image
|
|
||||||
* in many applications.
|
|
||||||
*
|
|
||||||
* @author Daniel Nilsson
|
|
||||||
*/
|
|
||||||
public class AlphaPatternDrawable extends Drawable {
|
|
||||||
|
|
||||||
private final int mAlphaPatternSize;
|
|
||||||
|
|
||||||
private int mNumRectanglesHorizontal;
|
|
||||||
private int mNumRectanglesVertical;
|
|
||||||
|
|
||||||
private final Rect mRect = new Rect(), mBounds = new Rect();
|
|
||||||
private final Paint mPaint = new Paint();
|
|
||||||
|
|
||||||
public AlphaPatternDrawable(final int alphaPatternSize) {
|
|
||||||
mAlphaPatternSize = alphaPatternSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(@NonNull final Canvas canvas) {
|
|
||||||
|
|
||||||
boolean verticalStartWhite = true;
|
|
||||||
for (int i = 0; i <= mNumRectanglesVertical; i++) {
|
|
||||||
boolean horizontalStartWhite = verticalStartWhite;
|
|
||||||
for (int j = 0; j <= mNumRectanglesHorizontal; j++) {
|
|
||||||
mRect.setEmpty();
|
|
||||||
mRect.top = i * mAlphaPatternSize + mBounds.top;
|
|
||||||
mRect.left = j * mAlphaPatternSize + mBounds.left;
|
|
||||||
mRect.bottom = Math.min(mRect.top + mAlphaPatternSize, mBounds.bottom);
|
|
||||||
mRect.right = Math.min(mRect.left + mAlphaPatternSize, mBounds.right);
|
|
||||||
|
|
||||||
mPaint.setColor(horizontalStartWhite ? Color.WHITE : Color.LTGRAY);
|
|
||||||
canvas.drawRect(mRect, mPaint);
|
|
||||||
|
|
||||||
horizontalStartWhite = !horizontalStartWhite;
|
|
||||||
}
|
|
||||||
verticalStartWhite = !verticalStartWhite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() {
|
|
||||||
return PixelFormat.OPAQUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlpha(final int alpha) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColorFilter(final ColorFilter cf) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBoundsChange(final Rect bounds) {
|
|
||||||
super.onBoundsChange(bounds);
|
|
||||||
mBounds.set(bounds);
|
|
||||||
final int height = bounds.height();
|
|
||||||
final int width = bounds.width();
|
|
||||||
mNumRectanglesHorizontal = (int) Math.ceil(width / mAlphaPatternSize);
|
|
||||||
mNumRectanglesVertical = (int) Math.ceil(height / mAlphaPatternSize);
|
|
||||||
invalidateSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,69 +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.graphic;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
|
|
||||||
public class ColorPreviewDrawable extends AlphaPatternDrawable {
|
|
||||||
|
|
||||||
private final int mColor;
|
|
||||||
private final Paint mPaint;
|
|
||||||
private final float[] mPoints;
|
|
||||||
|
|
||||||
public ColorPreviewDrawable(final int alphaPatternSize, final int color) {
|
|
||||||
super(alphaPatternSize);
|
|
||||||
mPaint = new Paint();
|
|
||||||
mPaint.setColor(Color.WHITE);
|
|
||||||
mPaint.setStrokeWidth(2.0f);
|
|
||||||
mColor = color;
|
|
||||||
mPoints = new float[16];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(final Canvas canvas) {
|
|
||||||
super.draw(canvas);
|
|
||||||
canvas.drawColor(mColor);
|
|
||||||
canvas.drawLines(mPoints, mPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBoundsChange(final Rect bounds) {
|
|
||||||
super.onBoundsChange(bounds);
|
|
||||||
mPoints[0] = bounds.top;
|
|
||||||
mPoints[1] = bounds.top;
|
|
||||||
mPoints[2] = bounds.right;
|
|
||||||
mPoints[3] = bounds.top;
|
|
||||||
mPoints[4] = bounds.top;
|
|
||||||
mPoints[5] = bounds.top;
|
|
||||||
mPoints[6] = bounds.top;
|
|
||||||
mPoints[7] = bounds.bottom;
|
|
||||||
mPoints[8] = bounds.right;
|
|
||||||
mPoints[9] = bounds.top;
|
|
||||||
mPoints[10] = bounds.right;
|
|
||||||
mPoints[11] = bounds.bottom;
|
|
||||||
mPoints[12] = bounds.top;
|
|
||||||
mPoints[13] = bounds.bottom;
|
|
||||||
mPoints[14] = bounds.right;
|
|
||||||
mPoints[15] = bounds.bottom;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,451 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2012 Wireless Designs, LLC
|
|
||||||
* <p>
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
* a copy of this software and associated documentation files (the
|
|
||||||
* "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
* permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
* the following conditions:
|
|
||||||
* <p>
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
* <p>
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mariotaku.twidere.graphic;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.ColorFilter;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.StaticLayout;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Drawable object that draws text. A TextDrawable accepts most of the same
|
|
||||||
* parameters that can be applied to {@link android.widget.TextView} for
|
|
||||||
* displaying and formatting text.
|
|
||||||
*
|
|
||||||
* Optionally, a {@link Path} may be supplied on which to draw the text.
|
|
||||||
*
|
|
||||||
* A TextDrawable has an intrinsic size equal to that required to draw all the
|
|
||||||
* text it has been supplied, when possible. In cases where a {@link Path} has
|
|
||||||
* been supplied, the caller must explicitly call
|
|
||||||
* {@link #setBounds(android.graphics.Rect) setBounds()} to provide the Drawable
|
|
||||||
* size based on the Path constraints.
|
|
||||||
*/
|
|
||||||
public class TextDrawable extends Drawable {
|
|
||||||
|
|
||||||
/* Platform XML constants for typeface */
|
|
||||||
private static final int SANS = 1;
|
|
||||||
private static final int SERIF = 2;
|
|
||||||
private static final int MONOSPACE = 3;
|
|
||||||
|
|
||||||
/* Resources for scaling values to the given device */
|
|
||||||
private final Resources mResources;
|
|
||||||
/* Paint to hold most drawing primitives for the text */
|
|
||||||
private final TextPaint mTextPaint;
|
|
||||||
/* Layout is used to measure and draw the text */
|
|
||||||
private StaticLayout mTextLayout;
|
|
||||||
/* Alignment of the text inside its bounds */
|
|
||||||
private Layout.Alignment mTextAlignment = Layout.Alignment.ALIGN_NORMAL;
|
|
||||||
/* Optional path on which to draw the text */
|
|
||||||
private Path mTextPath;
|
|
||||||
/* Stateful text color list */
|
|
||||||
private ColorStateList mTextColors;
|
|
||||||
/* Container for the bounds to be reported to widgets */
|
|
||||||
private final Rect mTextBounds;
|
|
||||||
/* Text string to draw */
|
|
||||||
private CharSequence mText = "";
|
|
||||||
|
|
||||||
/* Attribute lists to pull default values from the current theme */
|
|
||||||
private static final int[] themeAttributes = {android.R.attr.textAppearance};
|
|
||||||
private static final int[] appearanceAttributes = {android.R.attr.textSize, android.R.attr.typeface,
|
|
||||||
android.R.attr.textStyle, android.R.attr.textColor};
|
|
||||||
|
|
||||||
public TextDrawable(final Context context) {
|
|
||||||
super();
|
|
||||||
// Used to load and scale resource items
|
|
||||||
mResources = context.getResources();
|
|
||||||
// Definition of this drawables size
|
|
||||||
mTextBounds = new Rect();
|
|
||||||
// Paint to use for the text
|
|
||||||
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
mTextPaint.density = mResources.getDisplayMetrics().density;
|
|
||||||
mTextPaint.setDither(true);
|
|
||||||
|
|
||||||
int textSize = 15;
|
|
||||||
ColorStateList textColor = null;
|
|
||||||
int styleIndex = -1;
|
|
||||||
int typefaceIndex = -1;
|
|
||||||
|
|
||||||
// Set default parameters from the current theme
|
|
||||||
final TypedArray a = context.getTheme().obtainStyledAttributes(themeAttributes);
|
|
||||||
final int appearanceId = a.getResourceId(0, -1);
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
TypedArray ap = null;
|
|
||||||
if (appearanceId != -1) {
|
|
||||||
ap = context.obtainStyledAttributes(appearanceId, appearanceAttributes);
|
|
||||||
}
|
|
||||||
if (ap != null) {
|
|
||||||
for (int i = 0; i < ap.getIndexCount(); i++) {
|
|
||||||
final int attr = ap.getIndex(i);
|
|
||||||
switch (attr) {
|
|
||||||
case 0: // Text Size
|
|
||||||
textSize = a.getDimensionPixelSize(attr, textSize);
|
|
||||||
break;
|
|
||||||
case 1: // Typeface
|
|
||||||
typefaceIndex = a.getInt(attr, typefaceIndex);
|
|
||||||
break;
|
|
||||||
case 2: // Text Style
|
|
||||||
styleIndex = a.getInt(attr, styleIndex);
|
|
||||||
break;
|
|
||||||
case 3: // Text Color
|
|
||||||
textColor = a.getColorStateList(attr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ap.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
|
|
||||||
setRawTextSize(textSize);
|
|
||||||
|
|
||||||
Typeface tf = null;
|
|
||||||
switch (typefaceIndex) {
|
|
||||||
case SANS:
|
|
||||||
tf = Typeface.SANS_SERIF;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SERIF:
|
|
||||||
tf = Typeface.SERIF;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MONOSPACE:
|
|
||||||
tf = Typeface.MONOSPACE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTypeface(tf, styleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(@NonNull final Canvas canvas) {
|
|
||||||
if (mTextPath == null) {
|
|
||||||
// Allow the layout to draw the text
|
|
||||||
mTextLayout.draw(canvas);
|
|
||||||
} else {
|
|
||||||
// Draw directly on the canvas using the supplied path
|
|
||||||
canvas.drawTextOnPath(mText.toString(), mTextPath, 0, 0, mTextPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIntrinsicHeight() {
|
|
||||||
// Return the vertical bounds measured, or -1 if none
|
|
||||||
if (mTextBounds.isEmpty())
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return mTextBounds.bottom - mTextBounds.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIntrinsicWidth() {
|
|
||||||
// Return the horizontal bounds measured, or -1 if none
|
|
||||||
if (mTextBounds.isEmpty())
|
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return mTextBounds.right - mTextBounds.left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() {
|
|
||||||
return PixelFormat.TRANSLUCENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the text currently being displayed
|
|
||||||
*/
|
|
||||||
public CharSequence getText() {
|
|
||||||
return mText;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current text alignment setting
|
|
||||||
*/
|
|
||||||
public Layout.Alignment getTextAlign() {
|
|
||||||
return mTextAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the horizontal stretch factor of the text
|
|
||||||
*/
|
|
||||||
public float getTextScaleX() {
|
|
||||||
return mTextPaint.getTextScaleX();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current text size, in pixels
|
|
||||||
*/
|
|
||||||
public float getTextSize() {
|
|
||||||
return mTextPaint.getTextSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current typeface and style that the Paint using for display.
|
|
||||||
*/
|
|
||||||
public Typeface getTypeface() {
|
|
||||||
return mTextPaint.getTypeface();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStateful() {
|
|
||||||
/*
|
|
||||||
* The drawable's ability to represent state is based on the text color
|
|
||||||
* list set
|
|
||||||
*/
|
|
||||||
return mTextColors.isStateful();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlpha(final int alpha) {
|
|
||||||
if (mTextPaint.getAlpha() != alpha) {
|
|
||||||
mTextPaint.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColorFilter(final ColorFilter cf) {
|
|
||||||
if (mTextPaint.getColorFilter() != cf) {
|
|
||||||
mTextPaint.setColorFilter(cf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text that will be displayed
|
|
||||||
*
|
|
||||||
* @param text Text to display
|
|
||||||
*/
|
|
||||||
public void setText(CharSequence text) {
|
|
||||||
if (text == null) {
|
|
||||||
text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
mText = text;
|
|
||||||
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text alignment. The alignment itself is based on the text layout
|
|
||||||
* direction. For LTR text NORMAL is left aligned and OPPOSITE is right
|
|
||||||
* aligned. For RTL text, those alignments are reversed.
|
|
||||||
*
|
|
||||||
* @param align Text alignment value. Should be set to one of:
|
|
||||||
*
|
|
||||||
* {@link Layout.Alignment#ALIGN_NORMAL},
|
|
||||||
* {@link Layout.Alignment#ALIGN_NORMAL},
|
|
||||||
* {@link Layout.Alignment#ALIGN_OPPOSITE}.
|
|
||||||
*/
|
|
||||||
public void setTextAlign(final Layout.Alignment align) {
|
|
||||||
if (mTextAlignment != align) {
|
|
||||||
mTextAlignment = align;
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text color as a state list
|
|
||||||
*
|
|
||||||
* @param colorStateList ColorStateList of text colors, such as inflated
|
|
||||||
* from an R.color resource
|
|
||||||
*/
|
|
||||||
public void setTextColor(final ColorStateList colorStateList) {
|
|
||||||
mTextColors = colorStateList;
|
|
||||||
updateTextColors(getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a single text color for all states
|
|
||||||
*
|
|
||||||
* @param color Color value such as {@link Color#WHITE} or
|
|
||||||
* {@link Color#argb(int, int, int, int)}
|
|
||||||
*/
|
|
||||||
public void setTextColor(final int color) {
|
|
||||||
setTextColor(ColorStateList.valueOf(color));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional Path object on which to draw the text. If this is set,
|
|
||||||
* TextDrawable cannot properly measure the bounds this drawable will need.
|
|
||||||
* You must call {@link #setBounds(int, int, int, int) setBounds()} before
|
|
||||||
* applying this TextDrawable to any View.
|
|
||||||
*
|
|
||||||
* Calling this method with <code>null</code> will remove any Path currently
|
|
||||||
* attached.
|
|
||||||
*/
|
|
||||||
public void setTextPath(final Path path) {
|
|
||||||
if (mTextPath != path) {
|
|
||||||
mTextPath = path;
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the horizontal stretch factor of the text
|
|
||||||
*
|
|
||||||
* @param size Text scale factor
|
|
||||||
*/
|
|
||||||
public void setTextScaleX(final float size) {
|
|
||||||
if (size != mTextPaint.getTextScaleX()) {
|
|
||||||
mTextPaint.setTextScaleX(size);
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text size. The value will be interpreted in "sp" units
|
|
||||||
*
|
|
||||||
* @param size Text size value, in sp
|
|
||||||
*/
|
|
||||||
public void setTextSize(final float size) {
|
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the text size, using the supplied complex units
|
|
||||||
*
|
|
||||||
* @param unit Units for the text size, such as dp or sp
|
|
||||||
* @param size Text size value
|
|
||||||
*/
|
|
||||||
public void setTextSize(final int unit, final float size) {
|
|
||||||
final float dimension = TypedValue.applyDimension(unit, size, mResources.getDisplayMetrics());
|
|
||||||
setRawTextSize(dimension);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the typeface and style in which the text should be displayed. Note
|
|
||||||
* that not all Typeface families actually have bold and italic variants, so
|
|
||||||
* you may need to use {@link #setTypeface(Typeface, int)} to get the
|
|
||||||
* appearance that you actually want.
|
|
||||||
*/
|
|
||||||
public void setTypeface(final Typeface tf) {
|
|
||||||
if (mTextPaint.getTypeface() != tf) {
|
|
||||||
mTextPaint.setTypeface(tf);
|
|
||||||
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the typeface and style in which the text should be displayed, and
|
|
||||||
* turns on the fake bold and italic bits in the Paint if the Typeface that
|
|
||||||
* you provided does not have all the bits in the style that you specified.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void setTypeface(Typeface tf, final int style) {
|
|
||||||
if (style > 0) {
|
|
||||||
if (tf == null) {
|
|
||||||
tf = Typeface.defaultFromStyle(style);
|
|
||||||
} else {
|
|
||||||
tf = Typeface.create(tf, style);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTypeface(tf);
|
|
||||||
// now compute what (if any) algorithmic styling is needed
|
|
||||||
final int typefaceStyle = tf != null ? tf.getStyle() : 0;
|
|
||||||
final int need = style & ~typefaceStyle;
|
|
||||||
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
|
|
||||||
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
|
|
||||||
} else {
|
|
||||||
mTextPaint.setFakeBoldText(false);
|
|
||||||
mTextPaint.setTextSkewX(0);
|
|
||||||
setTypeface(tf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBoundsChange(final Rect bounds) {
|
|
||||||
// Update the internal bounds in response to any external requests
|
|
||||||
mTextBounds.set(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onStateChange(final int[] state) {
|
|
||||||
// Upon state changes, grab the correct text color
|
|
||||||
return updateTextColors(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method to take measurements of the current contents and apply
|
|
||||||
* the correct bounds when possible.
|
|
||||||
*/
|
|
||||||
private void measureContent() {
|
|
||||||
// If drawing to a path, we cannot measure intrinsic bounds
|
|
||||||
// We must resly on setBounds being called externally
|
|
||||||
if (mTextPath != null) {
|
|
||||||
// Clear any previous measurement
|
|
||||||
mTextLayout = null;
|
|
||||||
mTextBounds.setEmpty();
|
|
||||||
} else {
|
|
||||||
// Measure text bounds
|
|
||||||
final float desired = Layout.getDesiredWidth(mText, mTextPaint);
|
|
||||||
mTextLayout = new StaticLayout(mText, mTextPaint, (int) desired, mTextAlignment, 1.0f, 0.0f, false);
|
|
||||||
mTextBounds.set(0, 0, mTextLayout.getWidth(), mTextLayout.getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
// We may need to be redrawn
|
|
||||||
invalidateSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the text size, in raw pixels
|
|
||||||
*/
|
|
||||||
private void setRawTextSize(final float size) {
|
|
||||||
if (size != mTextPaint.getTextSize()) {
|
|
||||||
mTextPaint.setTextSize(size);
|
|
||||||
|
|
||||||
measureContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method to apply the correct text color based on the drawable's
|
|
||||||
* state
|
|
||||||
*/
|
|
||||||
private boolean updateTextColors(final int[] stateSet) {
|
|
||||||
final int newColor = mTextColors.getColorForState(stateSet, Color.WHITE);
|
|
||||||
if (mTextPaint.getColor() != newColor) {
|
|
||||||
mTextPaint.setColor(newColor);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,117 +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.loader;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.v4.content.AsyncTaskLoader;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.mariotaku.microblog.library.MicroBlog;
|
|
||||||
import org.mariotaku.microblog.library.MicroBlogException;
|
|
||||||
import org.mariotaku.microblog.library.statusnet.model.Group;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.CursorSupport;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.PageableResponseList;
|
|
||||||
import org.mariotaku.twidere.TwidereConstants;
|
|
||||||
import org.mariotaku.twidere.loader.iface.ICursorSupportLoader;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableGroup;
|
|
||||||
import org.mariotaku.twidere.model.UserKey;
|
|
||||||
import org.mariotaku.twidere.model.util.ParcelableGroupUtils;
|
|
||||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
|
|
||||||
import org.mariotaku.twidere.util.collection.NoDuplicatesArrayList;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseGroupsLoader extends AsyncTaskLoader<List<ParcelableGroup>>
|
|
||||||
implements TwidereConstants, ICursorSupportLoader {
|
|
||||||
|
|
||||||
protected final NoDuplicatesArrayList<ParcelableGroup> mData = new NoDuplicatesArrayList<>();
|
|
||||||
protected final UserKey mAccountId;
|
|
||||||
private final long mCursor;
|
|
||||||
|
|
||||||
private long mNextCursor, mPrevCursor;
|
|
||||||
|
|
||||||
public BaseGroupsLoader(final Context context, final UserKey accountKey, final long cursor,
|
|
||||||
final List<ParcelableGroup> data) {
|
|
||||||
super(context);
|
|
||||||
if (data != null) {
|
|
||||||
mData.addAll(data);
|
|
||||||
}
|
|
||||||
mCursor = cursor;
|
|
||||||
mAccountId = accountKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getCursor() {
|
|
||||||
return mCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getNextCursor() {
|
|
||||||
return mNextCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getPrevCursor() {
|
|
||||||
return mPrevCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract List<Group> getGroups(final MicroBlog twitter) throws MicroBlogException;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ParcelableGroup> loadInBackground() {
|
|
||||||
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(getContext(), mAccountId);
|
|
||||||
List<Group> listLoaded = null;
|
|
||||||
try {
|
|
||||||
listLoaded = getGroups(twitter);
|
|
||||||
} catch (final MicroBlogException e) {
|
|
||||||
Log.w(LOGTAG, e);
|
|
||||||
}
|
|
||||||
if (listLoaded != null) {
|
|
||||||
final int listSize = listLoaded.size();
|
|
||||||
if (listLoaded instanceof PageableResponseList) {
|
|
||||||
mNextCursor = ((CursorSupport) listLoaded).getNextCursor();
|
|
||||||
mPrevCursor = ((CursorSupport) listLoaded).getPreviousCursor();
|
|
||||||
final int dataSize = mData.size();
|
|
||||||
for (int i = 0; i < listSize; i++) {
|
|
||||||
final Group group = listLoaded.get(i);
|
|
||||||
mData.add(ParcelableGroupUtils.from(group, mAccountId, dataSize + i, isMember(group)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < listSize; i++) {
|
|
||||||
final Group list = listLoaded.get(i);
|
|
||||||
mData.add(ParcelableGroupUtils.from(listLoaded.get(i), mAccountId, i, isMember(list)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Collections.sort(mData);
|
|
||||||
return mData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartLoading() {
|
|
||||||
forceLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isMember(final Group list) {
|
|
||||||
return list.isMember();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +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.loader;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.mariotaku.microblog.library.MicroBlog;
|
|
||||||
import org.mariotaku.microblog.library.MicroBlogException;
|
|
||||||
import org.mariotaku.microblog.library.statusnet.model.Group;
|
|
||||||
import org.mariotaku.microblog.library.twitter.model.ResponseList;
|
|
||||||
import org.mariotaku.twidere.model.ParcelableGroup;
|
|
||||||
import org.mariotaku.twidere.model.UserKey;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class UserGroupsLoader extends BaseGroupsLoader {
|
|
||||||
|
|
||||||
private final UserKey mUserKey;
|
|
||||||
private final String mScreenName;
|
|
||||||
|
|
||||||
public UserGroupsLoader(final Context context, final UserKey accountKey, final UserKey userKey,
|
|
||||||
final String screenName, final List<ParcelableGroup> data) {
|
|
||||||
super(context, accountKey, 0, data);
|
|
||||||
mUserKey = userKey;
|
|
||||||
mScreenName = screenName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResponseList<Group> getGroups(final MicroBlog twitter) throws MicroBlogException {
|
|
||||||
if (twitter == null) return null;
|
|
||||||
if (mUserKey != null) {
|
|
||||||
return twitter.getGroups(mUserKey.getId());
|
|
||||||
} else if (mScreenName != null) {
|
|
||||||
return twitter.getGroups(mScreenName);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isMember(final Group list) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,6 +14,7 @@ import org.mariotaku.restfu.annotation.method.GET;
|
||||||
import org.mariotaku.restfu.http.HttpRequest;
|
import org.mariotaku.restfu.http.HttpRequest;
|
||||||
import org.mariotaku.restfu.http.HttpResponse;
|
import org.mariotaku.restfu.http.HttpResponse;
|
||||||
import org.mariotaku.restfu.http.RestHttpClient;
|
import org.mariotaku.restfu.http.RestHttpClient;
|
||||||
|
import org.mariotaku.twidere.util.Utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ public class DefaultFeatures {
|
||||||
jsonParser.skipChildren();
|
jsonParser.skipChildren();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
response.close();
|
Utils.closeSilently(response);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +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.model;
|
|
||||||
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import static org.mariotaku.twidere.util.CompareUtils.bundleEquals;
|
|
||||||
import static org.mariotaku.twidere.util.CompareUtils.objectEquals;
|
|
||||||
|
|
||||||
public class TabSpec {
|
|
||||||
|
|
||||||
public CharSequence name;
|
|
||||||
public final Object icon;
|
|
||||||
public final Class<? extends Fragment> cls;
|
|
||||||
public final Bundle args;
|
|
||||||
public final int position;
|
|
||||||
|
|
||||||
public TabSpec(final CharSequence name, final Object icon, final Class<? extends Fragment> cls, final Bundle args,
|
|
||||||
final int position) {
|
|
||||||
if (cls == null) throw new IllegalArgumentException("Fragment cannot be null!");
|
|
||||||
if (name == null && icon == null)
|
|
||||||
throw new IllegalArgumentException("You must specify a name or icon for this tab!");
|
|
||||||
this.name = name;
|
|
||||||
this.icon = icon;
|
|
||||||
this.cls = cls;
|
|
||||||
this.args = args;
|
|
||||||
this.position = position;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(final Object o) {
|
|
||||||
if (!(o instanceof TabSpec)) return false;
|
|
||||||
final TabSpec spec = (TabSpec) o;
|
|
||||||
return objectEquals(name, spec.name) && objectEquals(icon, spec.icon) && cls == spec.cls
|
|
||||||
&& bundleEquals(args, spec.args) && position == spec.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TabSpec{name=" + name + ", icon=" + icon + ", cls=" + cls + ", args=" + args + ", position=" + position
|
|
||||||
+ "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -19,14 +19,17 @@
|
||||||
|
|
||||||
package org.mariotaku.twidere.model.message;
|
package org.mariotaku.twidere.model.message;
|
||||||
|
|
||||||
|
import org.mariotaku.twidere.model.UserKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Called when account changed
|
||||||
* Created by mariotaku on 15/4/24.
|
* Created by mariotaku on 15/4/24.
|
||||||
*/
|
*/
|
||||||
public class AccountChangedEvent {
|
public class AccountChangedEvent {
|
||||||
public final long[] account_ids, activated_ids;
|
public final UserKey[] account_keys, activated_keys;
|
||||||
|
|
||||||
public AccountChangedEvent(long[] account_ids, long[] activated_ids) {
|
public AccountChangedEvent(UserKey[] account_keys, UserKey[] activated_keys) {
|
||||||
this.account_ids = account_ids;
|
this.account_keys = account_keys;
|
||||||
this.activated_ids = activated_ids;
|
this.activated_keys = activated_keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class GetMessagesTaskEvent {
|
||||||
@NonNull
|
@NonNull
|
||||||
public final Uri uri;
|
public final Uri uri;
|
||||||
public final boolean running;
|
public final boolean running;
|
||||||
private final Exception exception;
|
public final Exception exception;
|
||||||
|
|
||||||
public GetMessagesTaskEvent(@NonNull Uri uri, boolean running, Exception exception) {
|
public GetMessagesTaskEvent(@NonNull Uri uri, boolean running, Exception exception) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
|
|
|
@ -1,26 +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.model.message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/3/23.
|
|
||||||
*/
|
|
||||||
public class VideoLoadFinishedEvent {
|
|
||||||
}
|
|
|
@ -3,9 +3,9 @@ package org.mariotaku.twidere.model.tab.iface;
|
||||||
import org.mariotaku.twidere.model.AccountDetails;
|
import org.mariotaku.twidere.model.AccountDetails;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Account callback for extra configurations
|
||||||
* Created by mariotaku on 2016/11/30.
|
* Created by mariotaku on 2016/11/30.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface AccountCallback {
|
public interface AccountCallback {
|
||||||
|
|
||||||
AccountDetails getAccount();
|
AccountDetails getAccount();
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package org.mariotaku.twidere.preference;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.PorterDuff.Mode;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.PreferenceViewHolder;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import org.mariotaku.twidere.R;
|
|
||||||
import org.mariotaku.twidere.util.ThemeUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 14-7-28.
|
|
||||||
*/
|
|
||||||
public class ForegroundColorIconPreference extends Preference {
|
|
||||||
public ForegroundColorIconPreference(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull PreferenceViewHolder view) {
|
|
||||||
super.onBindViewHolder(view);
|
|
||||||
final int fgColor = ThemeUtils.getThemeForegroundColor(getContext());
|
|
||||||
((ImageView) view.findViewById(android.R.id.icon)).setColorFilter(fgColor, Mode.SRC_ATOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForegroundColorIconPreference(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, R.attr.preferenceStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForegroundColorIconPreference(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +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.text.TextPaint;
|
|
||||||
import android.text.style.CharacterStyle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 14/12/13.
|
|
||||||
*/
|
|
||||||
public class TextAlphaSpan extends CharacterStyle {
|
|
||||||
|
|
||||||
private int alpha;
|
|
||||||
|
|
||||||
public TextAlphaSpan(int alpha) {
|
|
||||||
this.alpha = alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlpha(int alpha) {
|
|
||||||
this.alpha = alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateDrawState(TextPaint tp) {
|
|
||||||
tp.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -106,6 +106,7 @@ public abstract class AbsServiceInterface<I extends IInterface> implements IInte
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface CheckServiceAction {
|
public interface CheckServiceAction {
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
void check(@Nullable Bundle metaData) throws CheckServiceException;
|
void check(@Nullable Bundle metaData) throws CheckServiceException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -513,6 +513,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
public SingleResponse<Relationship> doLongOperation(Object param) {
|
public SingleResponse<Relationship> doLongOperation(Object param) {
|
||||||
final MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountKey);
|
final MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountKey);
|
||||||
try {
|
try {
|
||||||
|
if (microBlog == null) {
|
||||||
|
throw new MicroBlogException("No account");
|
||||||
|
}
|
||||||
final Relationship relationship = microBlog.updateFriendship(userKey.getId(), update);
|
final Relationship relationship = microBlog.updateFriendship(userKey.getId(), update);
|
||||||
if (!relationship.isSourceWantRetweetsFromTarget()) {
|
if (!relationship.isSourceWantRetweetsFromTarget()) {
|
||||||
// TODO remove cached retweets
|
// TODO remove cached retweets
|
||||||
|
@ -555,6 +558,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountId);
|
MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountId);
|
||||||
if (!Utils.isOfficialCredentials(context, accountId)) continue;
|
if (!Utils.isOfficialCredentials(context, accountId)) continue;
|
||||||
try {
|
try {
|
||||||
|
if (microBlog == null) {
|
||||||
|
throw new MicroBlogException("No account");
|
||||||
|
}
|
||||||
microBlog.setActivitiesAboutMeUnread(cursor);
|
microBlog.setActivitiesAboutMeUnread(cursor);
|
||||||
} catch (MicroBlogException e) {
|
} catch (MicroBlogException e) {
|
||||||
DebugLog.w(LOGTAG, null, e);
|
DebugLog.w(LOGTAG, null, e);
|
||||||
|
@ -733,11 +739,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteCaches(final List<String> list) {
|
private void deleteCaches(final List<String> list) {
|
||||||
for (final Uri uri : DataStoreUtils.STATUSES_URIS) {
|
|
||||||
// TODO delete caches
|
|
||||||
// ContentResolverUtils.bulkDelete(mResolver, uri, Statuses.USER_ID, list,
|
|
||||||
// Statuses.ACCOUNT_ID + " = " + mAccountKey, false);
|
|
||||||
}
|
|
||||||
// I bet you don't want to see these users in your auto complete list.
|
// I bet you don't want to see these users in your auto complete list.
|
||||||
//TODO insert to blocked users data
|
//TODO insert to blocked users data
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
|
|
|
@ -98,11 +98,7 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static android.text.TextUtils.isEmpty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/11/28.
|
* Created by mariotaku on 15/11/28.
|
||||||
|
@ -115,8 +111,6 @@ public class DataStoreUtils implements Constants {
|
||||||
public static final Uri[] ACTIVITIES_URIS = new Uri[]{Activities.AboutMe.CONTENT_URI};
|
public static final Uri[] ACTIVITIES_URIS = new Uri[]{Activities.AboutMe.CONTENT_URI};
|
||||||
|
|
||||||
private static final UriMatcher CONTENT_PROVIDER_URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
|
private static final UriMatcher CONTENT_PROVIDER_URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
private static Map<UserKey, String> sAccountScreenNames = new HashMap<>();
|
|
||||||
private static Map<UserKey, String> sAccountNames = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Statuses.CONTENT_PATH,
|
CONTENT_PROVIDER_URI_MATCHER.addURI(TwidereDataStore.AUTHORITY, Statuses.CONTENT_PATH,
|
||||||
|
@ -338,9 +332,6 @@ public class DataStoreUtils implements Constants {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAccountName(@NonNull final Context context, final UserKey accountKey) {
|
public static String getAccountName(@NonNull final Context context, final UserKey accountKey) {
|
||||||
final String cached = sAccountNames.get(accountKey);
|
|
||||||
if (!isEmpty(cached)) return cached;
|
|
||||||
|
|
||||||
AccountManager am = AccountManager.get(context);
|
AccountManager am = AccountManager.get(context);
|
||||||
Account account = AccountUtils.findByAccountKey(am, accountKey);
|
Account account = AccountUtils.findByAccountKey(am, accountKey);
|
||||||
if (account == null) return null;
|
if (account == null) return null;
|
||||||
|
@ -350,13 +341,9 @@ public class DataStoreUtils implements Constants {
|
||||||
|
|
||||||
public static String getAccountScreenName(final Context context, final UserKey accountKey) {
|
public static String getAccountScreenName(final Context context, final UserKey accountKey) {
|
||||||
if (context == null) return null;
|
if (context == null) return null;
|
||||||
final String cached = sAccountScreenNames.get(accountKey);
|
|
||||||
if (!isEmpty(cached)) return cached;
|
|
||||||
|
|
||||||
AccountManager am = AccountManager.get(context);
|
AccountManager am = AccountManager.get(context);
|
||||||
Account account = AccountUtils.findByAccountKey(am, accountKey);
|
Account account = AccountUtils.findByAccountKey(am, accountKey);
|
||||||
if (account == null) return null;
|
if (account == null) return null;
|
||||||
|
|
||||||
return AccountExtensionsKt.getAccountUser(account, am).screen_name;
|
return AccountExtensionsKt.getAccountUser(account, am).screen_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,10 +720,6 @@ public class DataStoreUtils implements Constants {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearAccountName() {
|
|
||||||
sAccountScreenNames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isFilteringUser(Context context, UserKey userKey) {
|
public static boolean isFilteringUser(Context context, UserKey userKey) {
|
||||||
return isFilteringUser(context, userKey.toString());
|
return isFilteringUser(context, userKey.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,6 @@ public class HtmlEscapeHelper {
|
||||||
return ESCAPE_HTML.translate(text);
|
return ESCAPE_HTML.translate(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toHtml(final String string) {
|
|
||||||
if (string == null) return null;
|
|
||||||
return escape(string).replace("\n", "<br/>");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toPlainText(final String string) {
|
public static String toPlainText(final String string) {
|
||||||
if (string == null) return null;
|
if (string == null) return null;
|
||||||
return unescape(string.replace("<br/>", "\n").replaceAll("<!--.*?-->|<[^>]+>", ""));
|
return unescape(string.replace("<br/>", "\n").replaceAll("<!--.*?-->|<[^>]+>", ""));
|
||||||
|
|
|
@ -1,119 +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.util;
|
|
||||||
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.utils.IoUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class ImageValidator {
|
|
||||||
|
|
||||||
public static int INVALID = 0;
|
|
||||||
public static int VALID_FOR_BITMAP_FACTORY = 0x1;
|
|
||||||
public static int VALID_FOR_REGION_DECODER = 0x2;
|
|
||||||
public static int VALID_FOR_ALL = VALID_FOR_BITMAP_FACTORY | VALID_FOR_REGION_DECODER;
|
|
||||||
|
|
||||||
private static final byte[] PNG_HEAD = {(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
|
||||||
private static final byte[] PNG_TAIL = {0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82};
|
|
||||||
|
|
||||||
private static final byte[] JPEG_HEAD = {(byte) 0xFF, (byte) 0xD8};
|
|
||||||
private static final byte[] JPEG_TAIL = {(byte) 0xFF, (byte) 0xD9};
|
|
||||||
|
|
||||||
private ImageValidator() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValidForRegionDecoder(int validity) {
|
|
||||||
return (validity & VALID_FOR_REGION_DECODER) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValid(int validity) {
|
|
||||||
return validity != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int checkImageValidity(final File file) {
|
|
||||||
if (file == null) return INVALID;
|
|
||||||
return checkImageValidity(file.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int checkImageValidity(final String file) {
|
|
||||||
final BitmapFactory.Options opts = new BitmapFactory.Options();
|
|
||||||
opts.inJustDecodeBounds = true;
|
|
||||||
BitmapFactory.decodeFile(file, opts);
|
|
||||||
final String type = opts.outMimeType;
|
|
||||||
if (type == null) return INVALID;
|
|
||||||
if ("image/jpeg".equalsIgnoreCase(type))
|
|
||||||
return checkJPEGValidity(file);
|
|
||||||
else if ("image/png".equalsIgnoreCase(type)) return checkPNGValidity(file);
|
|
||||||
return opts.outWidth > 0 && opts.outHeight > 0 ? VALID_FOR_BITMAP_FACTORY : INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int checkImageValidity(final Uri uri) {
|
|
||||||
if (uri == null) return INVALID;
|
|
||||||
return checkImageValidity(uri.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int checkJPEGValidity(final String file) {
|
|
||||||
return checkHeadTailValidity(file, JPEG_HEAD, JPEG_TAIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int checkPNGValidity(final String file) {
|
|
||||||
return checkHeadTailValidity(file, PNG_HEAD, PNG_TAIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int checkHeadTailValidity(final RandomAccessFile raf, final byte[] head, final byte[] tail) {
|
|
||||||
if (raf == null) return INVALID;
|
|
||||||
try {
|
|
||||||
final long length = raf.length();
|
|
||||||
// The file has 0-length, so it can't be a PNG file.
|
|
||||||
if (length == 0) return INVALID;
|
|
||||||
byte[] buffer;
|
|
||||||
// Read head.
|
|
||||||
buffer = new byte[head.length];
|
|
||||||
raf.seek(0);
|
|
||||||
if (raf.read(buffer) != buffer.length || !Arrays.equals(buffer, head)) return INVALID;
|
|
||||||
// Read tail.
|
|
||||||
buffer = new byte[tail.length];
|
|
||||||
raf.seek(length - buffer.length);
|
|
||||||
if (raf.read(buffer) != buffer.length || !Arrays.equals(buffer, tail))
|
|
||||||
return VALID_FOR_BITMAP_FACTORY;
|
|
||||||
} catch (final IOException e) {
|
|
||||||
return INVALID;
|
|
||||||
} finally {
|
|
||||||
IoUtils.closeSilently(raf);
|
|
||||||
}
|
|
||||||
return VALID_FOR_ALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int checkHeadTailValidity(final String file, final byte[] head, final byte[] tail) {
|
|
||||||
try {
|
|
||||||
return checkHeadTailValidity(new RandomAccessFile(file, "r"), head, tail);
|
|
||||||
} catch (final FileNotFoundException e) {
|
|
||||||
return INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +1,6 @@
|
||||||
package org.mariotaku.twidere.util;
|
package org.mariotaku.twidere.util;
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.JsonWriter;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.mariotaku.restfu.RestFuUtils;
|
|
||||||
import org.mariotaku.twidere.TwidereConstants;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/3/8.
|
* Created by mariotaku on 16/3/8.
|
||||||
|
@ -19,46 +9,6 @@ public class InternalParseUtils {
|
||||||
private InternalParseUtils() {
|
private InternalParseUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String bundleToJSON(final Bundle args) {
|
|
||||||
final Set<String> keys = args.keySet();
|
|
||||||
final StringWriter sw = new StringWriter();
|
|
||||||
final JsonWriter json = new JsonWriter(sw);
|
|
||||||
try {
|
|
||||||
json.beginObject();
|
|
||||||
for (final String key : keys) {
|
|
||||||
json.name(key);
|
|
||||||
final Object value = args.get(key);
|
|
||||||
if (value == null) {
|
|
||||||
json.nullValue();
|
|
||||||
} else if (value instanceof Boolean) {
|
|
||||||
json.value((Boolean) value);
|
|
||||||
} else if (value instanceof Integer) {
|
|
||||||
json.value((Integer) value);
|
|
||||||
} else if (value instanceof Long) {
|
|
||||||
json.value((Long) value);
|
|
||||||
} else if (value instanceof String) {
|
|
||||||
json.value((String) value);
|
|
||||||
} else if (value instanceof Float) {
|
|
||||||
json.value((Float) value);
|
|
||||||
} else if (value instanceof Double) {
|
|
||||||
json.value((Double) value);
|
|
||||||
} else {
|
|
||||||
json.nullValue();
|
|
||||||
Log.w(TwidereConstants.LOGTAG, "Unknown type " + value.getClass().getSimpleName() + " in arguments key " + key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
json.endObject();
|
|
||||||
json.flush();
|
|
||||||
sw.flush();
|
|
||||||
return sw.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
RestFuUtils.closeSilently(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String parsePrettyDecimal(double num, int decimalDigits) {
|
public static String parsePrettyDecimal(double num, int decimalDigits) {
|
||||||
String result = String.format(Locale.US, "%." + decimalDigits + "f", num);
|
String result = String.format(Locale.US, "%." + decimalDigits + "f", num);
|
||||||
int dotIdx = result.lastIndexOf('.');
|
int dotIdx = result.lastIndexOf('.');
|
||||||
|
|
|
@ -1,56 +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.util;
|
|
||||||
|
|
||||||
import android.support.v4.util.LongSparseArray;
|
|
||||||
|
|
||||||
public class LongSparseArrayUtils {
|
|
||||||
private LongSparseArrayUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A copy of all keys contained in the sparse array.
|
|
||||||
*/
|
|
||||||
public static <E> long[] getKeys(final LongSparseArray<E> array) {
|
|
||||||
final int length = array.size();
|
|
||||||
final long[] result = new long[length];
|
|
||||||
for (int i = 0, j = length; i < j; i++) {
|
|
||||||
result[i] = array.keyAt(i);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <E> boolean hasKey(final LongSparseArray<E> array, final long key) {
|
|
||||||
return array.indexOfKey(key) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets all supplied keys to the given unique value.
|
|
||||||
*
|
|
||||||
* @param keys Keys to set
|
|
||||||
* @param uniqueValue Value to set all supplied keys to
|
|
||||||
*/
|
|
||||||
public static <E> void setValues(final LongSparseArray<E> array, final long[] keys, final E uniqueValue) {
|
|
||||||
final int length = keys.length;
|
|
||||||
for (long key : keys) {
|
|
||||||
array.put(key, uniqueValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package org.mariotaku.twidere.util;
|
|
||||||
|
|
||||||
import org.mariotaku.microblog.library.MicroBlogException;
|
|
||||||
import org.mariotaku.restfu.RestAPIFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 16/5/27.
|
|
||||||
*/
|
|
||||||
public class MicroBlogBuilder {
|
|
||||||
|
|
||||||
final RestAPIFactory<MicroBlogException> factory;
|
|
||||||
|
|
||||||
public MicroBlogBuilder() {
|
|
||||||
factory = new RestAPIFactory<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public <T> T build(Class<T> cls) {
|
|
||||||
return factory.build(cls);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -37,10 +37,6 @@ public final class ServiceUtils {
|
||||||
private ServiceUtils() {
|
private ServiceUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServiceToken bindToService(final Context context, final Intent intent) {
|
|
||||||
return bindToService(context, intent, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ServiceToken bindToService(final Context context, final Intent intent,
|
public static ServiceToken bindToService(final Context context, final Intent intent,
|
||||||
final ServiceConnection callback) {
|
final ServiceConnection callback) {
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff.Mode;
|
import android.graphics.PorterDuff.Mode;
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -165,10 +164,6 @@ public class ThemeUtils implements Constants {
|
||||||
return TwidereColorUtils.YIQToColor(Color.alpha(accentColor), yiq);
|
return TwidereColorUtils.YIQToColor(Color.alpha(accentColor), yiq);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Resources getResources(final Context context) {
|
|
||||||
return context.getResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Drawable getSelectableItemBackgroundDrawable(final Context context) {
|
public static Drawable getSelectableItemBackgroundDrawable(final Context context) {
|
||||||
return getDrawableFromThemeAttribute(context, android.R.attr.selectableItemBackground);
|
return getDrawableFromThemeAttribute(context, android.R.attr.selectableItemBackground);
|
||||||
}
|
}
|
||||||
|
@ -304,15 +299,6 @@ public class ThemeUtils implements Constants {
|
||||||
ThemeBackgroundPreference.MIN_ALPHA, ThemeBackgroundPreference.MAX_ALPHA);
|
ThemeBackgroundPreference.MIN_ALPHA, ThemeBackgroundPreference.MAX_ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Typeface getUserTypeface(final Context context, final String fontFamily, final Typeface defTypeface) {
|
|
||||||
if (context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
|
||||||
return Typeface.DEFAULT;
|
|
||||||
final int fontStyle = defTypeface != null ? defTypeface.getStyle() : Typeface.NORMAL;
|
|
||||||
final Typeface tf = Typeface.create(fontFamily, fontStyle);
|
|
||||||
if (tf != null) return tf;
|
|
||||||
return Typeface.create(Typeface.DEFAULT, fontStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Drawable getWindowBackground(final Context context) {
|
public static Drawable getWindowBackground(final Context context) {
|
||||||
final TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.windowBackground});
|
final TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.windowBackground});
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,138 +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.animation.Animator;
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.TypeEvaluator;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static utility methods for Transitions.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class TransitionUtils {
|
|
||||||
private static int MAX_IMAGE_SIZE = (1024 * 1024);
|
|
||||||
|
|
||||||
private TransitionUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static Animator mergeAnimators(Animator animator1, Animator animator2) {
|
|
||||||
if (animator1 == null) {
|
|
||||||
return animator2;
|
|
||||||
} else if (animator2 == null) {
|
|
||||||
return animator1;
|
|
||||||
} else {
|
|
||||||
AnimatorSet animatorSet = new AnimatorSet();
|
|
||||||
animatorSet.playTogether(animator1, animator2);
|
|
||||||
return animatorSet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a copy of bitmap of given drawable, return null if intrinsic size is zero
|
|
||||||
*/
|
|
||||||
public static Bitmap createDrawableBitmap(Drawable drawable) {
|
|
||||||
int width = drawable.getIntrinsicWidth();
|
|
||||||
int height = drawable.getIntrinsicHeight();
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (width * height));
|
|
||||||
if (drawable instanceof BitmapDrawable && scale == 1f) {
|
|
||||||
// return same bitmap if scale down not needed
|
|
||||||
return ((BitmapDrawable) drawable).getBitmap();
|
|
||||||
}
|
|
||||||
int bitmapWidth = (int) (width * scale);
|
|
||||||
int bitmapHeight = (int) (height * scale);
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
Rect existingBounds = drawable.getBounds();
|
|
||||||
int left = existingBounds.left;
|
|
||||||
int top = existingBounds.top;
|
|
||||||
int right = existingBounds.right;
|
|
||||||
int bottom = existingBounds.bottom;
|
|
||||||
drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
|
|
||||||
drawable.draw(canvas);
|
|
||||||
drawable.setBounds(left, top, right, bottom);
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
|
|
||||||
* coordinates. <code>matrix</code> will be modified during the bitmap creation.
|
|
||||||
* <p/>
|
|
||||||
* <p>If the bitmap is large, it will be scaled uniformly down to at most 1MB size.</p>
|
|
||||||
*
|
|
||||||
* @param view The view to create a bitmap for.
|
|
||||||
* @param matrix The matrix converting the view local coordinates to the coordinates that
|
|
||||||
* the bitmap will be displayed in. <code>matrix</code> will be modified before
|
|
||||||
* returning.
|
|
||||||
* @param bounds The bounds of the bitmap in the destination coordinate system (where the
|
|
||||||
* view should be presented. Typically, this is matrix.mapRect(viewBounds);
|
|
||||||
* @return A bitmap of the given view or null if bounds has no width or height.
|
|
||||||
*/
|
|
||||||
public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
int bitmapWidth = Math.round(bounds.width());
|
|
||||||
int bitmapHeight = Math.round(bounds.height());
|
|
||||||
if (bitmapWidth > 0 && bitmapHeight > 0) {
|
|
||||||
float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
|
|
||||||
bitmapWidth *= scale;
|
|
||||||
bitmapHeight *= scale;
|
|
||||||
matrix.postTranslate(-bounds.left, -bounds.top);
|
|
||||||
matrix.postScale(scale, scale);
|
|
||||||
bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
canvas.concat(matrix);
|
|
||||||
view.draw(canvas);
|
|
||||||
}
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MatrixEvaluator implements TypeEvaluator<Matrix> {
|
|
||||||
|
|
||||||
float[] mTempStartValues = new float[9];
|
|
||||||
|
|
||||||
float[] mTempEndValues = new float[9];
|
|
||||||
|
|
||||||
Matrix mTempMatrix = new Matrix();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
|
|
||||||
startValue.getValues(mTempStartValues);
|
|
||||||
endValue.getValues(mTempEndValues);
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
float diff = mTempEndValues[i] - mTempStartValues[i];
|
|
||||||
mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
|
|
||||||
}
|
|
||||||
mTempMatrix.setValues(mTempEndValues);
|
|
||||||
return mTempMatrix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,14 +20,10 @@
|
||||||
package org.mariotaku.twidere.util;
|
package org.mariotaku.twidere.util;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public final class TwidereArrayUtils {
|
public final class TwidereArrayUtils {
|
||||||
|
|
||||||
|
@ -43,15 +39,6 @@ public final class TwidereArrayUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean contentMatch(final long[] array1, final long[] array2) {
|
|
||||||
if (array1 == null || array2 == null) return array1 == array2;
|
|
||||||
if (array1.length != array2.length) return false;
|
|
||||||
for (long anArray1 : array1) {
|
|
||||||
if (!ArrayUtils.contains(array2, anArray1)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean contentMatch(final Object[] array1, final Object[] array2) {
|
public static boolean contentMatch(final Object[] array1, final Object[] array2) {
|
||||||
if (array1 == null || array2 == null) return array1 == array2;
|
if (array1 == null || array2 == null) return array1 == array2;
|
||||||
if (array1.length != array2.length) return false;
|
if (array1.length != array2.length) return false;
|
||||||
|
@ -61,40 +48,6 @@ public final class TwidereArrayUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long[] fromList(final List<Long> list) {
|
|
||||||
if (list == null) return null;
|
|
||||||
final int count = list.size();
|
|
||||||
final long[] array = new long[count];
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
array[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static long[] intersection(final long[] array1, final long[] array2) {
|
|
||||||
if (array1 == null || array2 == null) return new long[0];
|
|
||||||
final List<Long> list1 = new ArrayList<>();
|
|
||||||
for (final long item : array1) {
|
|
||||||
list1.add(item);
|
|
||||||
}
|
|
||||||
final List<Long> list2 = new ArrayList<>();
|
|
||||||
for (final long item : array2) {
|
|
||||||
list2.add(item);
|
|
||||||
}
|
|
||||||
list1.retainAll(list2);
|
|
||||||
return fromList(list1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T[] intersection(@NonNull final T[] array1, @NonNull final T[] array2) {
|
|
||||||
final List<T> list1 = new ArrayList<>();
|
|
||||||
Collections.addAll(list1, array1);
|
|
||||||
final List<T> list2 = new ArrayList<>();
|
|
||||||
Collections.addAll(list2, array2);
|
|
||||||
list1.retainAll(list2);
|
|
||||||
//noinspection unchecked
|
|
||||||
return list1.toArray((T[]) Array.newInstance(array1.getClass().getComponentType(), list1.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int arraysLength(@NonNull final Object... arrays) {
|
public static int arraysLength(@NonNull final Object... arrays) {
|
||||||
int length = 0;
|
int length = 0;
|
||||||
|
@ -116,21 +69,6 @@ public final class TwidereArrayUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static long[] parseLongArray(final String string, final char token) {
|
|
||||||
if (TextUtils.isEmpty(string)) return new long[0];
|
|
||||||
final String[] itemsStringArray = string.split(String.valueOf(token));
|
|
||||||
final long[] array = new long[itemsStringArray.length];
|
|
||||||
for (int i = 0, j = itemsStringArray.length; i < j; i++) {
|
|
||||||
try {
|
|
||||||
array[i] = Long.parseLong(itemsStringArray[i]);
|
|
||||||
} catch (final NumberFormatException e) {
|
|
||||||
return new long[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toString(final long[] array, final char token, final boolean include_space) {
|
public static String toString(final long[] array, final char token, final boolean include_space) {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
final int length = array.length;
|
final int length = array.length;
|
||||||
|
@ -173,18 +111,6 @@ public final class TwidereArrayUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String toStringForSQL(final String[] array) {
|
|
||||||
final int size = array != null ? array.length : 0;
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
builder.append(',');
|
|
||||||
}
|
|
||||||
builder.append('?');
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void offset(long[] array, long offset) {
|
public static void offset(long[] array, long offset) {
|
||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
array[i] += offset;
|
array[i] += offset;
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
package org.mariotaku.twidere.util;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 16/3/7.
|
|
||||||
*/
|
|
||||||
public class TwidereCollectionUtils {
|
|
||||||
private TwidereCollectionUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] toStringArray(final Collection<?> list) {
|
|
||||||
if (list == null) return null;
|
|
||||||
final int length = list.size();
|
|
||||||
final String[] stringArray = new String[length];
|
|
||||||
int idx = 0;
|
|
||||||
for (Object o : list) {
|
|
||||||
stringArray[idx++] = ParseUtils.parseString(o);
|
|
||||||
}
|
|
||||||
return stringArray;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -117,7 +117,4 @@ public class TwidereColorUtils {
|
||||||
return (yiq >= threshold) ? colorDark : colorLight;
|
return (yiq >= threshold) ? colorDark : colorLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getYIQContrast(int color1, int color2) {
|
|
||||||
return getYIQLuminance(color1) - getYIQLuminance(color2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +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.util;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class TwidereListUtils {
|
|
||||||
|
|
||||||
private TwidereListUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Long> fromArray(final long[] array) {
|
|
||||||
if (array == null) return null;
|
|
||||||
final List<Long> list = new ArrayList<>();
|
|
||||||
for (final long item : array) {
|
|
||||||
list.add(item);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> String toString(final List<T> list, final char delimiter, final boolean includeSpace) {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
final int size = list.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
builder.append(delimiter);
|
|
||||||
if (includeSpace) {
|
|
||||||
builder.append(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.append(list.get(i));
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -19,24 +19,11 @@
|
||||||
|
|
||||||
package org.mariotaku.twidere.util;
|
package org.mariotaku.twidere.util;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
public class TwidereMathUtils {
|
public class TwidereMathUtils {
|
||||||
public static final int RANGE_EXCLUSIVE_EXCLUSIVE = 0b00;
|
|
||||||
public static final int RANGE_EXCLUSIVE_INCLUSIVE = 0b01;
|
|
||||||
public static final int RANGE_INCLUSIVE_EXCLUSIVE = 0b10;
|
|
||||||
public static final int RANGE_INCLUSIVE_INCLUSIVE = 0b11;
|
|
||||||
static final int MASK_LEFT_BOUND = 0b10;
|
|
||||||
static final int MASK_RIGHT_BOUND = 0b01;
|
|
||||||
|
|
||||||
private TwidereMathUtils() {
|
private TwidereMathUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float clamp(final float num, final float bound1, final float bound2) {
|
|
||||||
final float max = Math.max(bound1, bound2), min = Math.min(bound1, bound2);
|
|
||||||
return Math.max(Math.min(num, max), min);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int clamp(final int num, final int bound1, final int bound2) {
|
public static int clamp(final int num, final int bound1, final int bound2) {
|
||||||
final int max = Math.max(bound1, bound2), min = Math.min(bound1, bound2);
|
final int max = Math.max(bound1, bound2), min = Math.min(bound1, bound2);
|
||||||
return Math.max(Math.min(num, max), min);
|
return Math.max(Math.min(num, max), min);
|
||||||
|
@ -57,43 +44,4 @@ public class TwidereMathUtils {
|
||||||
return n + 1;
|
return n + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the previous power of two.
|
|
||||||
// Returns the input if it is already power of 2.
|
|
||||||
// Throws IllegalArgumentException if the input is <= 0
|
|
||||||
public static int prevPowerOf2(final int n) {
|
|
||||||
if (n <= 0) throw new IllegalArgumentException();
|
|
||||||
return Integer.highestOneBit(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double sum(double... doubles) {
|
|
||||||
double sum = 0;
|
|
||||||
for (double d : doubles) {
|
|
||||||
sum += d;
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int sum(@NonNull int[] array) {
|
|
||||||
return sum(array, 0, array.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int sum(@NonNull int[] array, int start, int end) {
|
|
||||||
int sum = 0;
|
|
||||||
for (int i = start; i <= end; i++) {
|
|
||||||
int num = array[i];
|
|
||||||
sum += num;
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean inRange(int num, int from, int to, int flag) {
|
|
||||||
return ((flag & MASK_LEFT_BOUND) == 0 ? num > from : num >= from)
|
|
||||||
&& ((flag & MASK_RIGHT_BOUND) == 0 ? num < to : num <= to);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean inRange(float num, float from, float to, int flag) {
|
|
||||||
return ((flag & MASK_LEFT_BOUND) == 0 ? num > from : num >= from)
|
|
||||||
&& ((flag & MASK_RIGHT_BOUND) == 0 ? num < to : num <= to);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ public class TwidereStringUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix to https://github.com/TwidereProject/Twidere-Android/issues/449
|
* Fix to https://github.com/TwidereProject/Twidere-Android/issues/449
|
||||||
* @param string
|
|
||||||
*/
|
*/
|
||||||
public static void fixSHY(Spannable string) {
|
public static void fixSHY(Spannable string) {
|
||||||
for (int i = 0, j = string.length(); i < j; i++) {
|
for (int i = 0, j = string.length(); i < j; i++) {
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package org.mariotaku.twidere.util;
|
|
||||||
|
|
||||||
import android.support.annotation.UiThread;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 16/1/23.
|
|
||||||
*/
|
|
||||||
public class TwidereViewUtils {
|
|
||||||
private TwidereViewUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
public static boolean hitView(float x, float y, View view) {
|
|
||||||
int[] location = new int[2];
|
|
||||||
view.getLocationOnScreen(location);
|
|
||||||
return TwidereMathUtils.inRange(x, location[0], location[0] + view.getWidth(), TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE) &&
|
|
||||||
TwidereMathUtils.inRange(y, location[1], location[1] + view.getHeight(), TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +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.util;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
|
|
||||||
|
|
||||||
public class UnreadCountUtils {
|
|
||||||
private UnreadCountUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getUnreadCount(final Context context, final int position) {
|
|
||||||
if (context == null || position < 0) return 0;
|
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
|
||||||
final Uri.Builder builder = TwidereDataStore.UnreadCounts.CONTENT_URI.buildUpon();
|
|
||||||
builder.appendPath(ParseUtils.parseString(position));
|
|
||||||
final Uri uri = builder.build();
|
|
||||||
final Cursor c = resolver.query(uri, new String[] { UnreadCounts.COUNT }, null, null, null);
|
|
||||||
if (c == null) return 0;
|
|
||||||
try {
|
|
||||||
if (c.getCount() == 0) return 0;
|
|
||||||
c.moveToFirst();
|
|
||||||
return c.getInt(c.getColumnIndex(UnreadCounts.COUNT));
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getUnreadCount(final Context context, final String type) {
|
|
||||||
if (context == null || type == null) return 0;
|
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
|
||||||
final Uri.Builder builder = TwidereDataStore.UnreadCounts.ByType.CONTENT_URI.buildUpon();
|
|
||||||
builder.appendPath(type);
|
|
||||||
final Uri uri = builder.build();
|
|
||||||
final Cursor c = resolver.query(uri, new String[] { UnreadCounts.COUNT }, null, null, null);
|
|
||||||
if (c == null) return 0;
|
|
||||||
try {
|
|
||||||
if (c.getCount() == 0) return 0;
|
|
||||||
c.moveToFirst();
|
|
||||||
return c.getInt(c.getColumnIndex(UnreadCounts.COUNT));
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,10 +48,8 @@ public class UserColorNameManager {
|
||||||
private final SharedPreferences colorPreferences, nicknamePreferences;
|
private final SharedPreferences colorPreferences, nicknamePreferences;
|
||||||
private final LruCache<String, Integer> colorCache;
|
private final LruCache<String, Integer> colorCache;
|
||||||
private final LruCache<String, String> nicknameCache;
|
private final LruCache<String, String> nicknameCache;
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public UserColorNameManager(Context context) {
|
public UserColorNameManager(Context context) {
|
||||||
this.context = context;
|
|
||||||
colorPreferences = context.getSharedPreferences(USER_COLOR_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
colorPreferences = context.getSharedPreferences(USER_COLOR_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||||
nicknamePreferences = context.getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
nicknamePreferences = context.getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||||
colorCache = new LruCache<>(512);
|
colorCache = new LruCache<>(512);
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.mariotaku.twidere.util;
|
||||||
|
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
@ -43,7 +42,6 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.nfc.NfcAdapter;
|
import android.nfc.NfcAdapter;
|
||||||
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
|
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
|
||||||
|
@ -51,8 +49,6 @@ import android.os.BatteryManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
@ -63,12 +59,9 @@ import android.support.v4.view.GravityCompat;
|
||||||
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.view.menu.MenuBuilder;
|
import android.support.v7.view.menu.MenuBuilder;
|
||||||
import android.system.ErrnoException;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.transition.Transition;
|
|
||||||
import android.transition.TransitionInflater;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
@ -76,13 +69,9 @@ import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.AbsListView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
@ -123,10 +112,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
|
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
||||||
import org.mariotaku.twidere.util.TwidereLinkify.HighlightStyle;
|
|
||||||
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
|
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
|
||||||
import org.mariotaku.twidere.view.ShapedImageView;
|
|
||||||
import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle;
|
|
||||||
import org.mariotaku.twidere.view.TabPagerIndicator;
|
import org.mariotaku.twidere.view.TabPagerIndicator;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
@ -575,33 +561,6 @@ public final class Utils implements Constants {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getImagePathFromUri(final Context context, final Uri uri) {
|
|
||||||
if (context == null || uri == null) return null;
|
|
||||||
|
|
||||||
final String mediaUriStart = ParseUtils.parseString(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
|
||||||
|
|
||||||
if (ParseUtils.parseString(uri).startsWith(mediaUriStart)) {
|
|
||||||
|
|
||||||
final String[] proj = {MediaStore.Images.Media.DATA};
|
|
||||||
final Cursor cur = context.getContentResolver().query(uri, proj, null, null, null);
|
|
||||||
|
|
||||||
if (cur == null) return null;
|
|
||||||
|
|
||||||
final int idxData = cur.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
|
||||||
|
|
||||||
cur.moveToFirst();
|
|
||||||
try {
|
|
||||||
return cur.getString(idxData);
|
|
||||||
} finally {
|
|
||||||
cur.close();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final String path = uri.getPath();
|
|
||||||
if (path != null && new File(path).exists()) return path;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getMediaUploadStatus(@NonNull final Context context,
|
public static String getMediaUploadStatus(@NonNull final Context context,
|
||||||
@Nullable final CharSequence[] links,
|
@Nullable final CharSequence[] links,
|
||||||
@Nullable final CharSequence text) {
|
@Nullable final CharSequence text) {
|
||||||
|
@ -626,20 +585,6 @@ public final class Utils implements Constants {
|
||||||
return new File(context.getCacheDir(), cacheDirName);
|
return new File(context.getCacheDir(), cacheDirName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HighlightStyle
|
|
||||||
public static int getLinkHighlightingStyleInt(final String option) {
|
|
||||||
if (option == null) return VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE;
|
|
||||||
switch (option) {
|
|
||||||
case VALUE_LINK_HIGHLIGHT_OPTION_BOTH:
|
|
||||||
return VALUE_LINK_HIGHLIGHT_OPTION_CODE_BOTH;
|
|
||||||
case VALUE_LINK_HIGHLIGHT_OPTION_HIGHLIGHT:
|
|
||||||
return VALUE_LINK_HIGHLIGHT_OPTION_CODE_HIGHLIGHT;
|
|
||||||
case VALUE_LINK_HIGHLIGHT_OPTION_UNDERLINE:
|
|
||||||
return VALUE_LINK_HIGHLIGHT_OPTION_CODE_UNDERLINE;
|
|
||||||
}
|
|
||||||
return VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getLocalizedNumber(final Locale locale, final Number number) {
|
public static String getLocalizedNumber(final Locale locale, final Number number) {
|
||||||
final NumberFormat nf = NumberFormat.getInstance(locale);
|
final NumberFormat nf = NumberFormat.getInstance(locale);
|
||||||
return nf.format(number);
|
return nf.format(number);
|
||||||
|
@ -692,14 +637,6 @@ public final class Utils implements Constants {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShapeStyle
|
|
||||||
public static int getProfileImageStyle(String style) {
|
|
||||||
if (VALUE_PROFILE_IMAGE_STYLE_SQUARE.equalsIgnoreCase(style)) {
|
|
||||||
return ShapedImageView.SHAPE_RECTANGLE;
|
|
||||||
}
|
|
||||||
return ShapedImageView.SHAPE_CIRCLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewStyle
|
@PreviewStyle
|
||||||
public static int getMediaPreviewStyle(String style) {
|
public static int getMediaPreviewStyle(String style) {
|
||||||
if (VALUE_MEDIA_PREVIEW_STYLE_SCALE.equalsIgnoreCase(style)) {
|
if (VALUE_MEDIA_PREVIEW_STYLE_SCALE.equalsIgnoreCase(style)) {
|
||||||
|
@ -873,16 +810,6 @@ public final class Utils implements Constants {
|
||||||
return accountId.equals(retweetedById) || myRetweetId != null;
|
return accountId.equals(retweetedById) || myRetweetId != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNetworkAvailable(final Context context) {
|
|
||||||
try {
|
|
||||||
final ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
final NetworkInfo info = cm.getActiveNetworkInfo();
|
|
||||||
return info != null && info.isConnected();
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int matchTabCode(@Nullable final Uri uri) {
|
public static int matchTabCode(@Nullable final Uri uri) {
|
||||||
if (uri == null) return UriMatcher.NO_MATCH;
|
if (uri == null) return UriMatcher.NO_MATCH;
|
||||||
return HOME_TABS_URI_MATCHER.match(uri);
|
return HOME_TABS_URI_MATCHER.match(uri);
|
||||||
|
@ -956,47 +883,11 @@ public final class Utils implements Constants {
|
||||||
activity.overridePendingTransition(enterAnim, exitAnim);
|
activity.overridePendingTransition(enterAnim, exitAnim);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void scrollListToPosition(final AbsListView list, final int position) {
|
|
||||||
scrollListToPosition(list, position, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void scrollListToPosition(final AbsListView absListView, final int position, final int offset) {
|
|
||||||
if (absListView == null) return;
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
|
||||||
if (absListView instanceof ListView) {
|
|
||||||
final ListView listView = (ListView) absListView;
|
|
||||||
listView.setSelectionFromTop(position, offset);
|
|
||||||
} else {
|
|
||||||
absListView.setSelection(position);
|
|
||||||
}
|
|
||||||
stopListView(absListView);
|
|
||||||
} else {
|
|
||||||
stopListView(absListView);
|
|
||||||
if (absListView instanceof ListView) {
|
|
||||||
final ListView listView = (ListView) absListView;
|
|
||||||
listView.setSelectionFromTop(position, offset);
|
|
||||||
} else {
|
|
||||||
absListView.setSelection(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void scrollListToTop(final AbsListView list) {
|
|
||||||
if (list == null) return;
|
|
||||||
scrollListToPosition(list, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isMyStatus(ParcelableStatus status) {
|
static boolean isMyStatus(ParcelableStatus status) {
|
||||||
if (isMyRetweet(status)) return true;
|
if (isMyRetweet(status)) return true;
|
||||||
return status.account_key.maybeEquals(status.user_key);
|
return status.account_key.maybeEquals(status.user_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shouldStopAutoRefreshOnBatteryLow(final Context context) {
|
|
||||||
final SharedPreferences mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE);
|
|
||||||
return mPreferences.getBoolean(KEY_STOP_AUTO_REFRESH_WHEN_BATTERY_LOW, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showErrorMessage(final Context context, final CharSequence message, final boolean longMessage) {
|
public static void showErrorMessage(final Context context, final CharSequence message, final boolean longMessage) {
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
final Toast toast = Toast.makeText(context, message, longMessage ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
|
final Toast toast = Toast.makeText(context, message, longMessage ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
|
||||||
|
@ -1122,19 +1013,6 @@ public final class Utils implements Constants {
|
||||||
context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)));
|
context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void stopListView(final AbsListView list) {
|
|
||||||
if (list == null) return;
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
|
||||||
list.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
|
|
||||||
MotionEvent.ACTION_CANCEL, 0, 0, 0));
|
|
||||||
} else {
|
|
||||||
list.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
|
|
||||||
MotionEvent.ACTION_DOWN, 0, 0, 0));
|
|
||||||
list.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
|
|
||||||
MotionEvent.ACTION_UP, 0, 0, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String trimLineBreak(final String orig) {
|
public static String trimLineBreak(final String orig) {
|
||||||
if (orig == null) return null;
|
if (orig == null) return null;
|
||||||
return orig.replaceAll("\\n+", "\n");
|
return orig.replaceAll("\\n+", "\n");
|
||||||
|
@ -1228,21 +1106,10 @@ public final class Utils implements Constants {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCustomConsumerKeySecret(String consumerKey, String consumerSecret) {
|
|
||||||
if (TextUtils.isEmpty(consumerKey) || TextUtils.isEmpty(consumerSecret)) return false;
|
|
||||||
return !TWITTER_CONSUMER_KEY.equals(consumerKey) && !TWITTER_CONSUMER_SECRET.equals(consumerKey)
|
|
||||||
&& !TWITTER_CONSUMER_KEY_LEGACY.equals(consumerKey) && !TWITTER_CONSUMER_SECRET_LEGACY.equals(consumerSecret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isStreamingEnabled() {
|
public static boolean isStreamingEnabled() {
|
||||||
return Boolean.parseBoolean("false");
|
return Boolean.parseBoolean("false");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getErrorNo(Throwable t) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return 0;
|
|
||||||
return UtilsL.getErrorNo(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logOpenNotificationFromUri(Context context, Uri uri) {
|
public static void logOpenNotificationFromUri(Context context, Uri uri) {
|
||||||
if (!uri.getBooleanQueryParameter(QUERY_PARAM_FROM_NOTIFICATION, false)) return;
|
if (!uri.getBooleanQueryParameter(QUERY_PARAM_FROM_NOTIFICATION, false)) return;
|
||||||
final String type = uri.getQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE);
|
final String type = uri.getQueryParameter(QUERY_PARAM_NOTIFICATION_TYPE);
|
||||||
|
@ -1282,31 +1149,6 @@ public final class Utils implements Constants {
|
||||||
return !ConnectivityManagerCompat.isActiveNetworkMetered(cm) || !preferences.getBoolean(KEY_BANDWIDTH_SAVING_MODE);
|
return !ConnectivityManagerCompat.isActiveNetworkMetered(cm) || !preferences.getBoolean(KEY_BANDWIDTH_SAVING_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UtilsL {
|
|
||||||
|
|
||||||
private UtilsL() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
static void setSharedElementTransition(Context context, Window window, int transitionRes) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
|
|
||||||
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
|
|
||||||
final TransitionInflater inflater = TransitionInflater.from(context);
|
|
||||||
final Transition transition = inflater.inflateTransition(transitionRes);
|
|
||||||
window.setSharedElementEnterTransition(transition);
|
|
||||||
window.setSharedElementExitTransition(transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public static int getErrorNo(Throwable t) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return 0;
|
|
||||||
if (t instanceof ErrnoException) {
|
|
||||||
return ((ErrnoException) t).errno;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Notifications to Pebble smart watches
|
* Send Notifications to Pebble smart watches
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,47 +24,42 @@ import java.util.Collection;
|
||||||
|
|
||||||
public class NoDuplicatesArrayList<E> extends ArrayList<E> {
|
public class NoDuplicatesArrayList<E> extends ArrayList<E> {
|
||||||
|
|
||||||
private static final long serialVersionUID = -7277301117508689125L;
|
private static final long serialVersionUID = -7277301117508689125L;
|
||||||
|
|
||||||
public NoDuplicatesArrayList() {
|
public NoDuplicatesArrayList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoDuplicatesArrayList(final Collection<? extends E> collection) {
|
public NoDuplicatesArrayList(final Collection<? extends E> collection) {
|
||||||
addAll(collection);
|
addAll(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoDuplicatesArrayList(final int capacity) {
|
public NoDuplicatesArrayList(final int capacity) {
|
||||||
super(capacity);
|
super(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(final E e) {
|
public boolean add(final E e) {
|
||||||
if (contains(e))
|
if (contains(e)) return false;
|
||||||
return false;
|
return super.add(e);
|
||||||
else
|
}
|
||||||
return super.add(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(final int index, final E element) {
|
public void add(final int index, final E element) {
|
||||||
if (contains(element))
|
if (contains(element)) return;
|
||||||
return;
|
super.add(index, element);
|
||||||
else {
|
}
|
||||||
super.add(index, element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(final Collection<? extends E> collection) {
|
public boolean addAll(final Collection<? extends E> collection) {
|
||||||
final Collection<E> copy = new ArrayList<>(collection);
|
final Collection<E> copy = new ArrayList<>(collection);
|
||||||
copy.removeAll(this);
|
copy.removeAll(this);
|
||||||
return super.addAll(copy);
|
return super.addAll(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addAll(final int index, final Collection<? extends E> collection) {
|
public boolean addAll(final int index, final Collection<? extends E> collection) {
|
||||||
final Collection<E> copy = new ArrayList<>(collection);
|
final Collection<E> copy = new ArrayList<>(collection);
|
||||||
copy.removeAll(this);
|
copy.removeAll(this);
|
||||||
return super.addAll(index, copy);
|
return super.addAll(index, copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,371 +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.util.content;
|
|
||||||
|
|
||||||
import android.accounts.AccountManager;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import org.mariotaku.sqliteqb.library.Columns;
|
|
||||||
import org.mariotaku.sqliteqb.library.Columns.Column;
|
|
||||||
import org.mariotaku.sqliteqb.library.Constraint;
|
|
||||||
import org.mariotaku.sqliteqb.library.Expression;
|
|
||||||
import org.mariotaku.sqliteqb.library.NewColumn;
|
|
||||||
import org.mariotaku.sqliteqb.library.OnConflict;
|
|
||||||
import org.mariotaku.sqliteqb.library.RawSQLLang;
|
|
||||||
import org.mariotaku.sqliteqb.library.SQLQuery;
|
|
||||||
import org.mariotaku.sqliteqb.library.SQLQueryBuilder;
|
|
||||||
import org.mariotaku.sqliteqb.library.SetValue;
|
|
||||||
import org.mariotaku.sqliteqb.library.Table;
|
|
||||||
import org.mariotaku.sqliteqb.library.query.SQLCreateIndexQuery;
|
|
||||||
import org.mariotaku.sqliteqb.library.query.SQLCreateTableQuery;
|
|
||||||
import org.mariotaku.sqliteqb.library.query.SQLCreateTriggerQuery.Event;
|
|
||||||
import org.mariotaku.sqliteqb.library.query.SQLCreateTriggerQuery.Type;
|
|
||||||
import org.mariotaku.sqliteqb.library.query.SQLDeleteQuery;
|
|
||||||
import org.mariotaku.twidere.Constants;
|
|
||||||
import org.mariotaku.twidere.annotation.CustomTabType;
|
|
||||||
import org.mariotaku.twidere.model.Tab;
|
|
||||||
import org.mariotaku.twidere.model.TabValuesCreator;
|
|
||||||
import org.mariotaku.twidere.model.tab.TabConfiguration;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedHashtags;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedStatuses;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedTrends;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
|
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
|
|
||||||
import org.mariotaku.twidere.util.AccountMigratorKt;
|
|
||||||
import org.mariotaku.twidere.util.TwidereQueryBuilder.ConversationsEntryQueryBuilder;
|
|
||||||
import org.mariotaku.twidere.util.TwidereQueryBuilder.DirectMessagesQueryBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static org.mariotaku.twidere.util.content.DatabaseUpgradeHelper.safeUpgrade;
|
|
||||||
|
|
||||||
public final class TwidereSQLiteOpenHelper extends SQLiteOpenHelper implements Constants {
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
public TwidereSQLiteOpenHelper(final Context context, final String name, final int version) {
|
|
||||||
super(context, name, null, version);
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(final SQLiteDatabase db) {
|
|
||||||
db.beginTransaction();
|
|
||||||
db.execSQL(createTable(Statuses.TABLE_NAME, Statuses.COLUMNS, Statuses.TYPES, true));
|
|
||||||
db.execSQL(createTable(Activities.AboutMe.TABLE_NAME, Activities.AboutMe.COLUMNS, Activities.AboutMe.TYPES, true));
|
|
||||||
db.execSQL(createTable(Activities.ByFriends.TABLE_NAME, Activities.ByFriends.COLUMNS, Activities.ByFriends.TYPES, true));
|
|
||||||
db.execSQL(createTable(Drafts.TABLE_NAME, Drafts.COLUMNS, Drafts.TYPES, true));
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
db.execSQL(createTable(CachedUsers.TABLE_NAME, CachedUsers.COLUMNS, CachedUsers.TYPES, true,
|
|
||||||
createConflictReplaceConstraint(CachedUsers.USER_KEY)));
|
|
||||||
db.execSQL(createTable(CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES, true));
|
|
||||||
db.execSQL(createTable(CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS, CachedTrends.Local.TYPES,
|
|
||||||
true));
|
|
||||||
db.execSQL(createTable(CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES, true));
|
|
||||||
db.execSQL(createTable(CachedRelationships.TABLE_NAME, CachedRelationships.COLUMNS, CachedRelationships.TYPES, true,
|
|
||||||
createConflictReplaceConstraint(CachedRelationships.ACCOUNT_KEY, CachedRelationships.USER_KEY)));
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
db.execSQL(createTable(Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES, true));
|
|
||||||
db.execSQL(createTable(Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES, true));
|
|
||||||
db.execSQL(createTable(Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES, true));
|
|
||||||
db.execSQL(createTable(Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES, true));
|
|
||||||
db.execSQL(createTable(Filters.Subscriptions.TABLE_NAME, Filters.Subscriptions.COLUMNS, Filters.Subscriptions.TYPES, true));
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
db.execSQL(createTable(DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS,
|
|
||||||
DirectMessages.Inbox.TYPES, true));
|
|
||||||
db.execSQL(createTable(DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS,
|
|
||||||
DirectMessages.Outbox.TYPES, true));
|
|
||||||
db.execSQL(createTable(Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, true));
|
|
||||||
db.execSQL(createTable(SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true));
|
|
||||||
db.execSQL(createTable(SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true));
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
createViews(db);
|
|
||||||
createTriggers(db);
|
|
||||||
createIndices(db);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
|
|
||||||
setupDefaultTabs(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupDefaultTabs(SQLiteDatabase db) {
|
|
||||||
db.beginTransaction();
|
|
||||||
@CustomTabType
|
|
||||||
String[] tabTypes = {CustomTabType.HOME_TIMELINE, CustomTabType.NOTIFICATIONS_TIMELINE,
|
|
||||||
CustomTabType.TRENDS_SUGGESTIONS, CustomTabType.DIRECT_MESSAGES};
|
|
||||||
for (int i = 0, j = tabTypes.length; i < j; i++) {
|
|
||||||
@CustomTabType
|
|
||||||
final String tabType = tabTypes[i];
|
|
||||||
final TabConfiguration conf = TabConfiguration.ofType(tabType);
|
|
||||||
final Tab tab = new Tab();
|
|
||||||
tab.setType(tabType);
|
|
||||||
tab.setIcon(conf.getIcon().getPersistentKey());
|
|
||||||
tab.setPosition(i);
|
|
||||||
try {
|
|
||||||
db.insert(Tabs.TABLE_NAME, null, TabValuesCreator.create(tab));
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Constraint createConflictReplaceConstraint(String... columns) {
|
|
||||||
return Constraint.unique(new Columns(columns), OnConflict.IGNORE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createIndices(SQLiteDatabase db) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
|
|
||||||
db.execSQL(createIndex("statuses_index", Statuses.TABLE_NAME, new String[]{Statuses.ACCOUNT_KEY}, true));
|
|
||||||
db.execSQL(createIndex("messages_inbox_index", DirectMessages.Inbox.TABLE_NAME, new String[]{DirectMessages.ACCOUNT_KEY}, true));
|
|
||||||
db.execSQL(createIndex("messages_outbox_index", DirectMessages.Outbox.TABLE_NAME, new String[]{DirectMessages.ACCOUNT_KEY}, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createViews(SQLiteDatabase db) {
|
|
||||||
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.TABLE_NAME).getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.ConversationEntries.TABLE_NAME).getSQL());
|
|
||||||
|
|
||||||
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.TABLE_NAME)
|
|
||||||
.as(DirectMessagesQueryBuilder.build()).buildSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.ConversationEntries.TABLE_NAME)
|
|
||||||
.as(ConversationsEntryQueryBuilder.build()).buildSQL());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTriggers(SQLiteDatabase db) {
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_statuses").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_cached_statuses").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_received_messages").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_sent_messages").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "on_user_cache_update_trigger").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_cached_hashtags").getSQL());
|
|
||||||
db.execSQL(createDeleteDuplicateStatusTrigger("delete_old_statuses", Statuses.TABLE_NAME).getSQL());
|
|
||||||
db.execSQL(createDeleteDuplicateStatusTrigger("delete_old_cached_statuses", CachedStatuses.TABLE_NAME).getSQL());
|
|
||||||
db.execSQL(createDeleteDuplicateMessageTrigger("delete_old_received_messages", DirectMessages.Inbox.TABLE_NAME).getSQL());
|
|
||||||
db.execSQL(createDeleteDuplicateMessageTrigger("delete_old_sent_messages", DirectMessages.Outbox.TABLE_NAME).getSQL());
|
|
||||||
|
|
||||||
// Update user info in filtered users
|
|
||||||
final Table cachedUsersTable = new Table(CachedUsers.TABLE_NAME);
|
|
||||||
final Table filteredUsersTable = new Table(Filters.Users.TABLE_NAME);
|
|
||||||
db.execSQL(SQLQueryBuilder.createTrigger(false, true, "on_user_cache_update_trigger")
|
|
||||||
.type(Type.BEFORE)
|
|
||||||
.event(Event.INSERT)
|
|
||||||
.on(cachedUsersTable)
|
|
||||||
.forEachRow(true)
|
|
||||||
.actions(SQLQueryBuilder.update(OnConflict.REPLACE, filteredUsersTable)
|
|
||||||
.set(new SetValue(new Column(Filters.Users.NAME), new Column(Table.NEW, CachedUsers.NAME)),
|
|
||||||
new SetValue(new Column(Filters.Users.SCREEN_NAME), new Column(Table.NEW, CachedUsers.SCREEN_NAME)))
|
|
||||||
.where(Expression.equals(new Column(Filters.Users.USER_KEY), new Column(Table.NEW, CachedUsers.USER_KEY)))
|
|
||||||
.build())
|
|
||||||
.buildSQL());
|
|
||||||
|
|
||||||
// Delete duplicated hashtags ignoring case
|
|
||||||
final Table cachedHashtagsTable = new Table(CachedHashtags.TABLE_NAME);
|
|
||||||
db.execSQL(SQLQueryBuilder.createTrigger(false, true, "delete_old_cached_hashtags")
|
|
||||||
.type(Type.BEFORE)
|
|
||||||
.event(Event.INSERT)
|
|
||||||
.on(cachedHashtagsTable)
|
|
||||||
.forEachRow(true)
|
|
||||||
.actions(SQLQueryBuilder.deleteFrom(cachedHashtagsTable)
|
|
||||||
.where(Expression.like(new Column(CachedHashtags.NAME), new Column(Table.NEW, CachedHashtags.NAME)))
|
|
||||||
.build())
|
|
||||||
.buildSQL());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private SQLQuery createDeleteDuplicateStatusTrigger(String triggerName, String tableName) {
|
|
||||||
final Table table = new Table(tableName);
|
|
||||||
final SQLDeleteQuery deleteOld = SQLQueryBuilder.deleteFrom(table).where(Expression.and(
|
|
||||||
Expression.equals(new Column(Statuses.ACCOUNT_KEY), new Column(Table.NEW, Statuses.ACCOUNT_KEY)),
|
|
||||||
Expression.equals(new Column(Statuses.STATUS_ID), new Column(Table.NEW, Statuses.STATUS_ID))
|
|
||||||
)).build();
|
|
||||||
return SQLQueryBuilder.createTrigger(false, true, triggerName)
|
|
||||||
.type(Type.BEFORE).event(Event.INSERT).on(table).forEachRow(true)
|
|
||||||
.actions(deleteOld).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private SQLQuery createDeleteDuplicateMessageTrigger(String triggerName, String tableName) {
|
|
||||||
final Table table = new Table(tableName);
|
|
||||||
final SQLDeleteQuery deleteOld = SQLQueryBuilder.deleteFrom(table).where(Expression.and(
|
|
||||||
Expression.equals(new Column(DirectMessages.ACCOUNT_KEY), new Column(Table.NEW, DirectMessages.ACCOUNT_KEY)),
|
|
||||||
Expression.equals(new Column(DirectMessages.MESSAGE_ID), new Column(Table.NEW, DirectMessages.MESSAGE_ID))
|
|
||||||
)).build();
|
|
||||||
return SQLQueryBuilder.createTrigger(false, true, triggerName)
|
|
||||||
.type(Type.BEFORE).event(Event.INSERT).on(table).forEachRow(true)
|
|
||||||
.actions(deleteOld).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
|
||||||
handleVersionChange(db, oldVersion, newVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
|
||||||
handleVersionChange(db, oldVersion, newVersion);
|
|
||||||
if (oldVersion <= 43 && newVersion >= 44 && newVersion <= 153) {
|
|
||||||
final ContentValues values = new ContentValues();
|
|
||||||
final SharedPreferences prefs = mContext
|
|
||||||
.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
|
||||||
// Here I use old consumer key/secret because it's default key for
|
|
||||||
// older versions
|
|
||||||
final String prefConsumerKey = prefs.getString(KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY_LEGACY);
|
|
||||||
final String prefConsumerSecret = prefs.getString(KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET_LEGACY);
|
|
||||||
values.put(Accounts.CONSUMER_KEY, prefConsumerKey.trim());
|
|
||||||
values.put(Accounts.CONSUMER_SECRET, prefConsumerSecret.trim());
|
|
||||||
db.update(Accounts.TABLE_NAME, values, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleVersionChange(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
|
||||||
|
|
||||||
if (oldVersion <= 153) {
|
|
||||||
migrateLegacyAccounts(db);
|
|
||||||
if (newVersion > 153) {
|
|
||||||
AccountMigratorKt.migrateAccounts(AccountManager.get(mContext), db);
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTable(true, Accounts.TABLE_NAME).getSQL());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
safeUpgrade(db, Statuses.TABLE_NAME, Statuses.COLUMNS, Statuses.TYPES, true, null);
|
|
||||||
safeUpgrade(db, Activities.AboutMe.TABLE_NAME, Activities.AboutMe.COLUMNS,
|
|
||||||
Activities.AboutMe.TYPES, true, null);
|
|
||||||
safeUpgrade(db, Activities.ByFriends.TABLE_NAME, Activities.ByFriends.COLUMNS,
|
|
||||||
Activities.ByFriends.TYPES, true, null);
|
|
||||||
migrateDrafts(db);
|
|
||||||
safeUpgrade(db, CachedUsers.TABLE_NAME, CachedUsers.COLUMNS, CachedUsers.TYPES, true, null,
|
|
||||||
createConflictReplaceConstraint(CachedUsers.USER_KEY));
|
|
||||||
safeUpgrade(db, CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES, true, null);
|
|
||||||
safeUpgrade(db, CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES, true, null);
|
|
||||||
safeUpgrade(db, CachedRelationships.TABLE_NAME, CachedRelationships.COLUMNS, CachedRelationships.TYPES, true, null,
|
|
||||||
createConflictReplaceConstraint(CachedRelationships.ACCOUNT_KEY, CachedRelationships.USER_KEY));
|
|
||||||
|
|
||||||
migrateFilters(db, oldVersion);
|
|
||||||
safeUpgrade(db, DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS,
|
|
||||||
DirectMessages.Inbox.TYPES, true, null);
|
|
||||||
safeUpgrade(db, DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS,
|
|
||||||
DirectMessages.Outbox.TYPES, true, null);
|
|
||||||
safeUpgrade(db, CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS,
|
|
||||||
CachedTrends.Local.TYPES, true, null);
|
|
||||||
safeUpgrade(db, Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, false, null);
|
|
||||||
safeUpgrade(db, SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true, null);
|
|
||||||
safeUpgrade(db, SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true, null);
|
|
||||||
|
|
||||||
if (oldVersion < 131) {
|
|
||||||
migrateFilteredUsers(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTable(true, "network_usages").getSQL());
|
|
||||||
db.execSQL(SQLQueryBuilder.dropTable(true, "mentions").getSQL());
|
|
||||||
createViews(db);
|
|
||||||
createTriggers(db);
|
|
||||||
createIndices(db);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateDrafts(SQLiteDatabase db) {
|
|
||||||
final HashMap<String, String> draftsAlias = new HashMap<>();
|
|
||||||
draftsAlias.put(Drafts.MEDIA, "medias");
|
|
||||||
safeUpgrade(db, Drafts.TABLE_NAME, Drafts.COLUMNS, Drafts.TYPES, false, draftsAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateFilters(SQLiteDatabase db, int oldVersion) {
|
|
||||||
safeUpgrade(db, Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES,
|
|
||||||
oldVersion < 49, null);
|
|
||||||
|
|
||||||
final HashMap<String, String> filtersAlias = new HashMap<>();
|
|
||||||
safeUpgrade(db, Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES,
|
|
||||||
oldVersion < 49, filtersAlias);
|
|
||||||
safeUpgrade(db, Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES,
|
|
||||||
oldVersion < 49, filtersAlias);
|
|
||||||
safeUpgrade(db, Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES,
|
|
||||||
oldVersion < 49, filtersAlias);
|
|
||||||
safeUpgrade(db, Filters.Subscriptions.TABLE_NAME, Filters.Subscriptions.COLUMNS,
|
|
||||||
Filters.Subscriptions.TYPES, false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateLegacyAccounts(SQLiteDatabase db) {
|
|
||||||
final HashMap<String, String> accountsAlias = new HashMap<>();
|
|
||||||
accountsAlias.put(Accounts.SCREEN_NAME, "username");
|
|
||||||
accountsAlias.put(Accounts.NAME, "username");
|
|
||||||
accountsAlias.put(Accounts.ACCOUNT_KEY, "user_id");
|
|
||||||
accountsAlias.put(Accounts.COLOR, "user_color");
|
|
||||||
accountsAlias.put(Accounts.OAUTH_TOKEN_SECRET, "token_secret");
|
|
||||||
accountsAlias.put(Accounts.API_URL_FORMAT, "rest_base_url");
|
|
||||||
safeUpgrade(db, Accounts.TABLE_NAME, Accounts.COLUMNS, Accounts.TYPES, false, accountsAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateFilteredUsers(SQLiteDatabase db) {
|
|
||||||
db.execSQL(SQLQueryBuilder.update(OnConflict.REPLACE, Filters.Users.TABLE_NAME)
|
|
||||||
.set(new SetValue(Filters.Users.USER_KEY, new RawSQLLang(Filters.Users.USER_KEY + "||?")))
|
|
||||||
.where(Expression.notLikeArgs(new Column(Filters.Users.USER_KEY)))
|
|
||||||
.buildSQL(),
|
|
||||||
new Object[]{"@twitter.com", "%@%"});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String createTable(final String tableName, final String[] columns, final String[] types,
|
|
||||||
final boolean createIfNotExists, final Constraint... constraints) {
|
|
||||||
final SQLCreateTableQuery.Builder qb = SQLQueryBuilder.createTable(createIfNotExists, tableName);
|
|
||||||
qb.columns(NewColumn.createNewColumns(columns, types));
|
|
||||||
qb.constraint(constraints);
|
|
||||||
return qb.buildSQL();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String createIndex(final String indexName, final String tableName, final String[] columns,
|
|
||||||
final boolean createIfNotExists) {
|
|
||||||
final SQLCreateIndexQuery.Builder qb = SQLQueryBuilder.createIndex(false, createIfNotExists);
|
|
||||||
qb.name(indexName);
|
|
||||||
qb.on(new Table(tableName), new Columns(columns));
|
|
||||||
return qb.buildSQL();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
/*******************************************************************************
|
|
||||||
* Copyright 2011-2013 Sergey Tarasevich
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
package org.mariotaku.twidere.util.imageloader;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapShader;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.ColorFilter;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.graphics.Shader;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.assist.LoadedFrom;
|
|
||||||
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
|
|
||||||
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
|
|
||||||
import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can display bitmap with rounded corners. This implementation works only with ImageViews wrapped
|
|
||||||
* in ImageViewAware.
|
|
||||||
* <br />
|
|
||||||
* This implementation is inspired by
|
|
||||||
* <a href="http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/">
|
|
||||||
* Romain Guy's article</a>. It rounds images using custom drawable drawing. Original bitmap isn't changed.
|
|
||||||
* <br />
|
|
||||||
* <br />
|
|
||||||
* If this implementation doesn't meet your needs then consider
|
|
||||||
* <a href="https://github.com/vinc3m1/RoundedImageView">RoundedImageView</a> or
|
|
||||||
* <a href="https://github.com/Pkmmte/CircularImageView">CircularImageView</a> projects for usage.
|
|
||||||
*
|
|
||||||
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
|
|
||||||
* @since 1.5.6
|
|
||||||
*/
|
|
||||||
public class OvalBitmapDisplayer implements BitmapDisplayer {
|
|
||||||
|
|
||||||
protected final int margin;
|
|
||||||
|
|
||||||
public OvalBitmapDisplayer() {
|
|
||||||
this(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OvalBitmapDisplayer(int marginPixels) {
|
|
||||||
this.margin = marginPixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
|
|
||||||
if (!(imageAware instanceof ImageViewAware)) {
|
|
||||||
throw new IllegalArgumentException("ImageAware should wrap ImageView. ImageViewAware is expected.");
|
|
||||||
}
|
|
||||||
|
|
||||||
imageAware.setImageDrawable(new OvalDrawable(bitmap, margin));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class OvalDrawable extends Drawable {
|
|
||||||
|
|
||||||
protected final int margin;
|
|
||||||
|
|
||||||
protected final RectF mRect = new RectF(),
|
|
||||||
mBitmapRect;
|
|
||||||
protected final BitmapShader bitmapShader;
|
|
||||||
protected final Paint paint;
|
|
||||||
|
|
||||||
public OvalDrawable(Bitmap bitmap, int margin) {
|
|
||||||
this.margin = margin;
|
|
||||||
|
|
||||||
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
|
||||||
mBitmapRect = new RectF(margin, margin, bitmap.getWidth() - margin, bitmap.getHeight() - margin);
|
|
||||||
|
|
||||||
paint = new Paint();
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setShader(bitmapShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onBoundsChange(Rect bounds) {
|
|
||||||
super.onBoundsChange(bounds);
|
|
||||||
mRect.set(margin, margin, bounds.width() - margin, bounds.height() - margin);
|
|
||||||
|
|
||||||
// Resize the original bitmap to fit the new bound
|
|
||||||
Matrix shaderMatrix = new Matrix();
|
|
||||||
shaderMatrix.setRectToRect(mBitmapRect, mRect, Matrix.ScaleToFit.FILL);
|
|
||||||
bitmapShader.setLocalMatrix(shaderMatrix);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(@NonNull Canvas canvas) {
|
|
||||||
canvas.drawOval(mRect, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() {
|
|
||||||
return PixelFormat.TRANSLUCENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlpha(int alpha) {
|
|
||||||
paint.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColorFilter(ColorFilter cf) {
|
|
||||||
paint.setColorFilter(cf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package org.mariotaku.twidere.util.io;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 2016/12/8.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class CountOnlyOutputStream extends OutputStream {
|
|
||||||
private int count;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int i) throws IOException {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,15 +7,11 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import org.mariotaku.restfu.http.RestHttpClient;
|
import org.mariotaku.restfu.http.RestHttpClient;
|
||||||
import org.mariotaku.twidere.model.ParcelableMedia;
|
import org.mariotaku.twidere.model.ParcelableMedia;
|
||||||
import org.mariotaku.twidere.util.HtmlLinkExtractor;
|
|
||||||
import org.mariotaku.twidere.util.media.preview.provider.InstagramProvider;
|
import org.mariotaku.twidere.util.media.preview.provider.InstagramProvider;
|
||||||
import org.mariotaku.twidere.util.media.preview.provider.Provider;
|
import org.mariotaku.twidere.util.media.preview.provider.Provider;
|
||||||
import org.mariotaku.twidere.util.media.preview.provider.TwitterMediaProvider;
|
import org.mariotaku.twidere.util.media.preview.provider.TwitterMediaProvider;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/1/1.
|
* Created by mariotaku on 16/1/1.
|
||||||
|
@ -61,17 +57,4 @@ public class PreviewMediaExtractor {
|
||||||
return providerFor(link) != null;
|
return providerFor(link) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getSupportedLinksInStatus(final String statusString) {
|
|
||||||
if (statusString == null) return Collections.emptyList();
|
|
||||||
final List<String> links = new ArrayList<>();
|
|
||||||
final HtmlLinkExtractor extractor = new HtmlLinkExtractor();
|
|
||||||
for (final HtmlLinkExtractor.HtmlLink link : extractor.grabLinks(statusString)) {
|
|
||||||
final String linkString = link.getLink();
|
|
||||||
if (isSupported(linkString)) {
|
|
||||||
links.add(linkString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return links;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +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.support;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/11/18.
|
|
||||||
*/
|
|
||||||
public class IntentSupport {
|
|
||||||
@SuppressLint("InlinedApi")
|
|
||||||
public static final String CATEGORY_APP_BROWSER = Intent.CATEGORY_APP_BROWSER;
|
|
||||||
|
|
||||||
private IntentSupport() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setSelector(Intent intent, Intent selector) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) return;
|
|
||||||
IntentSupport15.setSelector(intent, selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
|
||||||
private static class IntentSupport15 {
|
|
||||||
public static void setSelector(Intent intent, Intent selector) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) return;
|
|
||||||
intent.setSelector(selector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,6 +37,7 @@ public class DefaultWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
|
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
|
||||||
try {
|
try {
|
||||||
mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||||
|
|
|
@ -89,7 +89,6 @@ public class ForegroundColorView extends View implements IForegroundView {
|
||||||
* the padding area.
|
* the padding area.
|
||||||
*
|
*
|
||||||
* @param drawable The Drawable to be drawn on top of the children.
|
* @param drawable The Drawable to be drawn on top of the children.
|
||||||
* @attr ref android.R.styleable#FrameLayout_foreground
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setForeground(final Drawable drawable) {
|
public void setForeground(final Drawable drawable) {
|
||||||
|
@ -102,7 +101,6 @@ public class ForegroundColorView extends View implements IForegroundView {
|
||||||
* Describes how the foreground is positioned. Defaults to START and TOP.
|
* Describes how the foreground is positioned. Defaults to START and TOP.
|
||||||
*
|
*
|
||||||
* @param foregroundGravity See {@link android.view.Gravity}
|
* @param foregroundGravity See {@link android.view.Gravity}
|
||||||
* @attr ref android.R.styleable#FrameLayout_foregroundGravity
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setForegroundGravity(final int foregroundGravity) {
|
public void setForegroundGravity(final int foregroundGravity) {
|
||||||
|
@ -111,7 +109,7 @@ public class ForegroundColorView extends View implements IForegroundView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAlphaPatternEnable(final boolean alphaPattern) {
|
public void setAlphaPatternEnabled(final boolean alphaPattern) {
|
||||||
if (mAlphaPattern == alphaPattern) return;
|
if (mAlphaPattern == alphaPattern) return;
|
||||||
mAlphaPattern = alphaPattern;
|
mAlphaPattern = alphaPattern;
|
||||||
invalidate();
|
invalidate();
|
||||||
|
|
|
@ -64,7 +64,6 @@ public class ForegroundImageView extends ImageView implements IForegroundView {
|
||||||
* the padding area.
|
* the padding area.
|
||||||
*
|
*
|
||||||
* @param drawable The Drawable to be drawn on top of the children.
|
* @param drawable The Drawable to be drawn on top of the children.
|
||||||
* @attr ref android.R.styleable#FrameLayout_foreground
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setForeground(final Drawable drawable) {
|
public void setForeground(final Drawable drawable) {
|
||||||
|
@ -77,7 +76,6 @@ public class ForegroundImageView extends ImageView implements IForegroundView {
|
||||||
* Describes how the foreground is positioned. Defaults to START and TOP.
|
* Describes how the foreground is positioned. Defaults to START and TOP.
|
||||||
*
|
*
|
||||||
* @param foregroundGravity See {@link android.view.Gravity}
|
* @param foregroundGravity See {@link android.view.Gravity}
|
||||||
* @attr ref android.R.styleable#FrameLayout_foregroundGravity
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setForegroundGravity(final int foregroundGravity) {
|
public void setForegroundGravity(final int foregroundGravity) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.view.MotionEventCompat;
|
import android.support.v4.view.MotionEventCompat;
|
||||||
import android.support.v4.view.ViewConfigurationCompat;
|
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
@ -92,7 +91,7 @@ public class LinePageIndicator extends View implements PagerIndicator {
|
||||||
a.recycle();
|
a.recycle();
|
||||||
|
|
||||||
final ViewConfiguration configuration = ViewConfiguration.get(context);
|
final ViewConfiguration configuration = ViewConfiguration.get(context);
|
||||||
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
|
mTouchSlop = configuration.getScaledPagingTouchSlop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getGapWidth() {
|
public float getGapWidth() {
|
||||||
|
@ -221,13 +220,13 @@ public class LinePageIndicator extends View implements PagerIndicator {
|
||||||
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
|
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
|
mActivePointerId = ev.getPointerId(0);
|
||||||
mLastMotionX = ev.getX();
|
mLastMotionX = ev.getX();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MotionEvent.ACTION_MOVE: {
|
case MotionEvent.ACTION_MOVE: {
|
||||||
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
|
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||||
final float x = MotionEventCompat.getX(ev, activePointerIndex);
|
final float x = ev.getX(activePointerIndex);
|
||||||
final float deltaX = x - mLastMotionX;
|
final float deltaX = x - mLastMotionX;
|
||||||
|
|
||||||
if (!mIsDragging) {
|
if (!mIsDragging) {
|
||||||
|
@ -276,19 +275,19 @@ public class LinePageIndicator extends View implements PagerIndicator {
|
||||||
|
|
||||||
case MotionEventCompat.ACTION_POINTER_DOWN: {
|
case MotionEventCompat.ACTION_POINTER_DOWN: {
|
||||||
final int index = MotionEventCompat.getActionIndex(ev);
|
final int index = MotionEventCompat.getActionIndex(ev);
|
||||||
mLastMotionX = MotionEventCompat.getX(ev, index);
|
mLastMotionX = ev.getX(index);
|
||||||
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
|
mActivePointerId = ev.getPointerId(index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MotionEventCompat.ACTION_POINTER_UP:
|
case MotionEventCompat.ACTION_POINTER_UP:
|
||||||
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
|
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
|
||||||
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
|
final int pointerId = ev.getPointerId(pointerIndex);
|
||||||
if (pointerId == mActivePointerId) {
|
if (pointerId == mActivePointerId) {
|
||||||
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
||||||
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
|
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||||
}
|
}
|
||||||
mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
|
mLastMotionX = MotionEventCompat.getX(ev, ev.findPointerIndex(mActivePointerId));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class ProfileBannerImageView extends ForegroundImageView implements IExte
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
protected boolean fitSystemWindows(@NonNull Rect insets) {
|
protected boolean fitSystemWindows(@NonNull Rect insets) {
|
||||||
if (mOnFitSystemWindowsListener != null) {
|
if (mOnFitSystemWindowsListener != null) {
|
||||||
mOnFitSystemWindowsListener.onFitSystemWindows(insets);
|
mOnFitSystemWindowsListener.onFitSystemWindows(insets);
|
||||||
|
|
|
@ -39,8 +39,4 @@ public class ViewListHolder {
|
||||||
return view.getContext();
|
return view.getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getString(final int resId, final Object... formatArgs) {
|
|
||||||
return getContext().getString(resId, formatArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +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.view.iface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 15/5/18.
|
|
||||||
*/
|
|
||||||
public interface ICustomTypefaceTextView {
|
|
||||||
}
|
|
|
@ -50,7 +50,6 @@ public interface IForegroundView {
|
||||||
* the padding area.
|
* the padding area.
|
||||||
*
|
*
|
||||||
* @param drawable The Drawable to be drawn on top of the children.
|
* @param drawable The Drawable to be drawn on top of the children.
|
||||||
* @attr ref android.R.attr#foreground
|
|
||||||
*/
|
*/
|
||||||
void setForeground(final Drawable drawable);
|
void setForeground(final Drawable drawable);
|
||||||
|
|
||||||
|
@ -58,7 +57,6 @@ public interface IForegroundView {
|
||||||
* Describes how the foreground is positioned. Defaults to START and TOP.
|
* Describes how the foreground is positioned. Defaults to START and TOP.
|
||||||
*
|
*
|
||||||
* @param foregroundGravity See {@link android.view.Gravity}
|
* @param foregroundGravity See {@link android.view.Gravity}
|
||||||
* @attr ref android.R.attr#foregroundGravity
|
|
||||||
*/
|
*/
|
||||||
void setForegroundGravity(int foregroundGravity);
|
void setForegroundGravity(int foregroundGravity);
|
||||||
|
|
||||||
|
|
|
@ -1,32 +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.view.iface;
|
|
||||||
|
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mariotaku on 14/12/19.
|
|
||||||
*/
|
|
||||||
public interface IThemeAccentView {
|
|
||||||
|
|
||||||
void setAccentTintColor(@NonNull ColorStateList color);
|
|
||||||
|
|
||||||
}
|
|
|
@ -40,30 +40,21 @@ public interface PagerIndicator extends ViewPager.OnPageChangeListener {
|
||||||
* This <strong>must</strong> be used if you need to set the page before the
|
* This <strong>must</strong> be used if you need to set the page before the
|
||||||
* views are drawn on screen (e.g., default start page).
|
* views are drawn on screen (e.g., default start page).
|
||||||
* </p>
|
* </p>
|
||||||
*
|
|
||||||
* @param item
|
|
||||||
*/
|
*/
|
||||||
void setCurrentItem(int item);
|
void setCurrentItem(int item);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a page change listener which will receive forwarded events.
|
* Set a page change listener which will receive forwarded events.
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
*/
|
||||||
void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
|
void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the indicator to a ViewPager.
|
* Bind the indicator to a ViewPager.
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
*/
|
*/
|
||||||
void setViewPager(ViewPager view);
|
void setViewPager(ViewPager view);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the indicator to a ViewPager.
|
* Bind the indicator to a ViewPager.
|
||||||
*
|
|
||||||
* @param view
|
|
||||||
* @param initialPosition
|
|
||||||
*/
|
*/
|
||||||
void setViewPager(ViewPager view, int initialPosition);
|
void setViewPager(ViewPager view, int initialPosition);
|
||||||
|
|
||||||
|
@ -85,17 +76,11 @@ public interface PagerIndicator extends ViewPager.OnPageChangeListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the icon of the view at position
|
* Returns the icon of the view at position
|
||||||
*
|
|
||||||
* @param position
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
Drawable getPageIcon(int position);
|
Drawable getPageIcon(int position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the title of the view at position
|
* Returns the title of the view at position
|
||||||
*
|
|
||||||
* @param position
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
CharSequence getPageTitle(int position);
|
CharSequence getPageTitle(int position);
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.mariotaku.chameleon.ChameleonUtils
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.kpreferences.set
|
import org.mariotaku.kpreferences.set
|
||||||
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
|
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
|
||||||
|
import org.mariotaku.ktextension.coerceInOr
|
||||||
import org.mariotaku.ktextension.convert
|
import org.mariotaku.ktextension.convert
|
||||||
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
|
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
|
||||||
import org.mariotaku.twidere.Constants.*
|
import org.mariotaku.twidere.Constants.*
|
||||||
|
@ -524,7 +525,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
val tabPosition = handleIntent(intent, false)
|
val tabPosition = handleIntent(intent, false)
|
||||||
if (tabPosition >= 0) {
|
if (tabPosition >= 0) {
|
||||||
mainPager.currentItem = TwidereMathUtils.clamp(tabPosition, pagerAdapter.count, 0)
|
mainPager.currentItem = tabPosition.coerceInOr(0 until pagerAdapter.count, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,10 +766,10 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
|
||||||
private fun setTabPosition(initialTab: Int) {
|
private fun setTabPosition(initialTab: Int) {
|
||||||
val rememberPosition = preferences.getBoolean(SharedPreferenceConstants.KEY_REMEMBER_POSITION, true)
|
val rememberPosition = preferences.getBoolean(SharedPreferenceConstants.KEY_REMEMBER_POSITION, true)
|
||||||
if (initialTab >= 0) {
|
if (initialTab >= 0) {
|
||||||
mainPager.currentItem = TwidereMathUtils.clamp(initialTab, pagerAdapter.count, 0)
|
mainPager.currentItem = initialTab.coerceInOr(0 until pagerAdapter.count, 0)
|
||||||
} else if (rememberPosition) {
|
} else if (rememberPosition) {
|
||||||
val position = preferences.getInt(SharedPreferenceConstants.KEY_SAVED_TAB_POSITION, 0)
|
val position = preferences.getInt(SharedPreferenceConstants.KEY_SAVED_TAB_POSITION, 0)
|
||||||
mainPager.currentItem = TwidereMathUtils.clamp(position, pagerAdapter.count, 0)
|
mainPager.currentItem = position.coerceInOr(0 until pagerAdapter.count, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import com.bluelinelabs.logansquare.LoganSquare
|
import com.bluelinelabs.logansquare.LoganSquare
|
||||||
|
import com.rengwuxian.materialedittext.MaterialEditText
|
||||||
import org.mariotaku.restfu.annotation.method.GET
|
import org.mariotaku.restfu.annotation.method.GET
|
||||||
import org.mariotaku.restfu.http.HttpRequest
|
import org.mariotaku.restfu.http.HttpRequest
|
||||||
import org.mariotaku.restfu.http.RestHttpClient
|
import org.mariotaku.restfu.http.RestHttpClient
|
||||||
|
@ -26,6 +27,7 @@ import org.mariotaku.twidere.model.CustomAPIConfig
|
||||||
import org.mariotaku.twidere.model.account.cred.Credentials
|
import org.mariotaku.twidere.model.account.cred.Credentials
|
||||||
import org.mariotaku.twidere.util.ParseUtils
|
import org.mariotaku.twidere.util.ParseUtils
|
||||||
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
|
||||||
|
import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -35,8 +37,8 @@ class APIEditorDialogFragment : BaseDialogFragment() {
|
||||||
private val editAPIUrlFormat by lazy { dialog.findViewById(R.id.editApiUrlFormat) as EditText }
|
private val editAPIUrlFormat by lazy { dialog.findViewById(R.id.editApiUrlFormat) as EditText }
|
||||||
private val editSameOAuthSigningUrl by lazy { dialog.findViewById(R.id.editSameOAuthSigningUrl) as CheckBox }
|
private val editSameOAuthSigningUrl by lazy { dialog.findViewById(R.id.editSameOAuthSigningUrl) as CheckBox }
|
||||||
private val editNoVersionSuffix by lazy { dialog.findViewById(R.id.editNoVersionSuffix) as CheckBox }
|
private val editNoVersionSuffix by lazy { dialog.findViewById(R.id.editNoVersionSuffix) as CheckBox }
|
||||||
private val editConsumerKey by lazy { dialog.findViewById(R.id.editConsumerKey) as EditText }
|
private val editConsumerKey by lazy { dialog.findViewById(R.id.editConsumerKey) as MaterialEditText }
|
||||||
private val editConsumerSecret by lazy { dialog.findViewById(R.id.editConsumerSecret) as EditText }
|
private val editConsumerSecret by lazy { dialog.findViewById(R.id.editConsumerSecret) as MaterialEditText }
|
||||||
private val editAuthType by lazy { dialog.findViewById(R.id.editAuthType) as RadioGroup }
|
private val editAuthType by lazy { dialog.findViewById(R.id.editAuthType) as RadioGroup }
|
||||||
private val apiFormatHelpButton by lazy { dialog.findViewById(R.id.apiUrlFormatHelp) }
|
private val apiFormatHelpButton by lazy { dialog.findViewById(R.id.apiUrlFormatHelp) }
|
||||||
private val accountTypeSpinner by lazy { dialog.findViewById(R.id.accountTypeSpinner) as Spinner }
|
private val accountTypeSpinner by lazy { dialog.findViewById(R.id.accountTypeSpinner) as Spinner }
|
||||||
|
@ -46,7 +48,7 @@ class APIEditorDialogFragment : BaseDialogFragment() {
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val builder = AlertDialog.Builder(context)
|
val builder = AlertDialog.Builder(context)
|
||||||
builder.setView(R.layout.layout_api_editor)
|
builder.setView(R.layout.dialog_api_editor)
|
||||||
builder.setPositiveButton(R.string.action_save) { dialog, which ->
|
builder.setPositiveButton(R.string.action_save) { dialog, which ->
|
||||||
val targetFragment = this.targetFragment
|
val targetFragment = this.targetFragment
|
||||||
val parentFragment = this.parentFragment
|
val parentFragment = this.parentFragment
|
||||||
|
@ -77,6 +79,10 @@ class APIEditorDialogFragment : BaseDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
accountTypeSpinner.adapter = AccountTypeSpinnerAdapter(context)
|
accountTypeSpinner.adapter = AccountTypeSpinnerAdapter(context)
|
||||||
|
|
||||||
|
editConsumerKey.addValidator(ConsumerKeySecretValidator(context.getString(R.string.invalid_consumer_key)))
|
||||||
|
editConsumerSecret.addValidator(ConsumerKeySecretValidator(context.getString(R.string.invalid_consumer_secret)))
|
||||||
|
|
||||||
editNoVersionSuffix.setOnCheckedChangeListener { buttonView, isChecked -> editNoVersionSuffixChanged = true }
|
editNoVersionSuffix.setOnCheckedChangeListener { buttonView, isChecked -> editNoVersionSuffixChanged = true }
|
||||||
editAuthType.setOnCheckedChangeListener { group, checkedId ->
|
editAuthType.setOnCheckedChangeListener { group, checkedId ->
|
||||||
val authType = getCheckedAuthType(checkedId)
|
val authType = getCheckedAuthType(checkedId)
|
||||||
|
|
|
@ -128,9 +128,9 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks<AccountsInfo>,
|
||||||
hasPrevAccountIndicator.alpha = 0f
|
hasPrevAccountIndicator.alpha = 0f
|
||||||
hasNextAccountIndicator.alpha = 0f
|
hasNextAccountIndicator.alpha = 0f
|
||||||
} else {
|
} else {
|
||||||
hasPrevAccountIndicator.alpha = TwidereMathUtils.clamp(pagePosition, 0f, 1f)
|
hasPrevAccountIndicator.alpha = pagePosition.coerceIn(0f, 1f)
|
||||||
hasNextAccountIndicator.alpha = TwidereMathUtils.clamp(pageCount - (pagePosition
|
hasNextAccountIndicator.alpha = (pageCount - (pagePosition + visiblePages))
|
||||||
+ visiblePages), 0f, 1f)
|
.coerceIn(0f, 1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1480,7 +1480,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
private var recyclerView: RecyclerView? = null
|
private var recyclerView: RecyclerView? = null
|
||||||
private var statusViewHolder: DetailStatusViewHolder? = null
|
private var statusViewHolder: DetailStatusViewHolder? = null
|
||||||
|
|
||||||
private val itemCounts: IntArray
|
private val itemCounts = ItemCounts(ITEM_TYPES_SUM)
|
||||||
|
|
||||||
override val nameFirst: Boolean
|
override val nameFirst: Boolean
|
||||||
private val cardBackgroundColor: Int
|
private val cardBackgroundColor: Int
|
||||||
|
@ -1525,7 +1525,6 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
init {
|
init {
|
||||||
setHasStableIds(true)
|
setHasStableIds(true)
|
||||||
val context = fragment.activity
|
val context = fragment.activity
|
||||||
itemCounts = IntArray(ITEM_TYPES_SUM)
|
|
||||||
// There's always a space at the end of the list
|
// There's always a space at the end of the list
|
||||||
itemCounts[ITEM_IDX_SPACE] = 1
|
itemCounts[ITEM_IDX_SPACE] = 1
|
||||||
itemCounts[ITEM_IDX_STATUS] = 1
|
itemCounts[ITEM_IDX_STATUS] = 1
|
||||||
|
@ -1570,7 +1569,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
|
|
||||||
fun getIndexStart(index: Int): Int {
|
fun getIndexStart(index: Int): Int {
|
||||||
if (index == 0) return 0
|
if (index == 0) return 0
|
||||||
return TwidereMathUtils.sum(itemCounts, 0, index - 1)
|
return itemCounts.getItemStartPosition(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStatusId(position: Int): String? {
|
override fun getStatusId(position: Int): String? {
|
||||||
|
@ -1827,7 +1826,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
if (status == null) return 0
|
if (status == null) return 0
|
||||||
return TwidereMathUtils.sum(itemCounts)
|
return itemCounts.itemCount
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {
|
override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {
|
||||||
|
|
|
@ -1398,7 +1398,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
||||||
|
|
||||||
private fun updateScrollOffset(offset: Int) {
|
private fun updateScrollOffset(offset: Int) {
|
||||||
val spaceHeight = profileBannerSpace.height
|
val spaceHeight = profileBannerSpace.height
|
||||||
val factor = TwidereMathUtils.clamp(if (spaceHeight == 0) 0f else offset / spaceHeight.toFloat(), 0f, 1f)
|
val factor = (if (spaceHeight == 0) 0f else offset / spaceHeight.toFloat()).coerceIn(0f, 1f)
|
||||||
profileBannerContainer.translationY = (-offset).toFloat()
|
profileBannerContainer.translationY = (-offset).toFloat()
|
||||||
profileBanner.translationY = (offset / 2).toFloat()
|
profileBanner.translationY = (offset / 2).toFloat()
|
||||||
profileBirthdayBanner.translationY = (offset / 2).toFloat()
|
profileBirthdayBanner.translationY = (offset / 2).toFloat()
|
||||||
|
@ -1417,7 +1417,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
||||||
val profileContentHeight = (profileNameContainer!!.height + profileDetailsContainer.height).toFloat()
|
val profileContentHeight = (profileNameContainer!!.height + profileDetailsContainer.height).toFloat()
|
||||||
val tabOutlineAlphaFactor: Float
|
val tabOutlineAlphaFactor: Float
|
||||||
if (offset - spaceHeight > 0) {
|
if (offset - spaceHeight > 0) {
|
||||||
tabOutlineAlphaFactor = 1f - TwidereMathUtils.clamp((offset - spaceHeight) / profileContentHeight, 0f, 1f)
|
tabOutlineAlphaFactor = 1f - ((offset - spaceHeight) / profileContentHeight).coerceIn(0f, 1f)
|
||||||
} else {
|
} else {
|
||||||
tabOutlineAlphaFactor = 1f
|
tabOutlineAlphaFactor = 1f
|
||||||
}
|
}
|
||||||
|
@ -1467,7 +1467,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
||||||
val location = IntArray(2)
|
val location = IntArray(2)
|
||||||
profileNameContainer.name.getLocationInWindow(location)
|
profileNameContainer.name.getLocationInWindow(location)
|
||||||
val nameShowingRatio = (userProfileDrawer.paddingTop - location[1]) / profileNameContainer.name.height.toFloat()
|
val nameShowingRatio = (userProfileDrawer.paddingTop - location[1]) / profileNameContainer.name.height.toFloat()
|
||||||
val textAlpha = TwidereMathUtils.clamp(nameShowingRatio, 0f, 1f)
|
val textAlpha = nameShowingRatio.coerceIn(0f, 1f)
|
||||||
val titleView = ViewSupport.findViewByText(toolbar, toolbar.title)
|
val titleView = ViewSupport.findViewByText(toolbar, toolbar.title)
|
||||||
if (titleView != null) {
|
if (titleView != null) {
|
||||||
titleView.alpha = textAlpha
|
titleView.alpha = textAlpha
|
||||||
|
@ -1545,10 +1545,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateValue() {
|
private fun updateValue() {
|
||||||
val shadowAlpha = Math.round(alpha * TwidereMathUtils.clamp(1 - factor, 0f, 1f))
|
val shadowAlpha = Math.round(alpha * (1 - factor).coerceIn(0f, 1f))
|
||||||
shadowDrawable.alpha = shadowAlpha
|
shadowDrawable.alpha = shadowAlpha
|
||||||
val hasColor = color != 0
|
val hasColor = color != 0
|
||||||
val colorAlpha = if (hasColor) Math.round(alpha * TwidereMathUtils.clamp(factor, 0f, 1f)) else 0
|
val colorAlpha = if (hasColor) Math.round(alpha * factor.coerceIn(0f, 1f)) else 0
|
||||||
colorDrawable.alpha = colorAlpha
|
colorDrawable.alpha = colorAlpha
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.v4.content.AsyncTaskLoader
|
||||||
|
import org.mariotaku.microblog.library.MicroBlog
|
||||||
|
import org.mariotaku.microblog.library.MicroBlogException
|
||||||
|
import org.mariotaku.microblog.library.statusnet.model.Group
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.CursorSupport
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.PageableResponseList
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||||
|
import org.mariotaku.twidere.loader.iface.ICursorSupportLoader
|
||||||
|
import org.mariotaku.twidere.model.ParcelableGroup
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableGroupUtils
|
||||||
|
import org.mariotaku.twidere.util.DebugLog
|
||||||
|
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||||
|
import org.mariotaku.twidere.util.collection.NoDuplicatesArrayList
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
abstract class BaseGroupsLoader(
|
||||||
|
context: Context,
|
||||||
|
protected val accountKey: UserKey,
|
||||||
|
override val cursor: Long,
|
||||||
|
data: List<ParcelableGroup>?
|
||||||
|
) : AsyncTaskLoader<List<ParcelableGroup>>(context), ICursorSupportLoader {
|
||||||
|
|
||||||
|
protected val data = NoDuplicatesArrayList<ParcelableGroup>()
|
||||||
|
|
||||||
|
override final var nextCursor: Long = 0
|
||||||
|
private set
|
||||||
|
override final var prevCursor: Long = 0
|
||||||
|
private set
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (data != null) {
|
||||||
|
this.data.addAll(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(MicroBlogException::class)
|
||||||
|
abstract fun getGroups(twitter: MicroBlog): List<Group>
|
||||||
|
|
||||||
|
override fun loadInBackground(): List<ParcelableGroup> {
|
||||||
|
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: return emptyList()
|
||||||
|
var listLoaded: List<Group>? = null
|
||||||
|
try {
|
||||||
|
listLoaded = getGroups(twitter)
|
||||||
|
} catch (e: MicroBlogException) {
|
||||||
|
DebugLog.w(LOGTAG, tr = e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listLoaded != null) {
|
||||||
|
val listSize = listLoaded.size
|
||||||
|
if (listLoaded is PageableResponseList<*>) {
|
||||||
|
nextCursor = (listLoaded as CursorSupport).nextCursor
|
||||||
|
prevCursor = listLoaded.previousCursor
|
||||||
|
val dataSize = data.size
|
||||||
|
for (i in 0..listSize - 1) {
|
||||||
|
val group = listLoaded[i]
|
||||||
|
data.add(ParcelableGroupUtils.from(group, accountKey, dataSize + i, isMember(group)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i in 0..listSize - 1) {
|
||||||
|
val list = listLoaded[i]
|
||||||
|
data.add(ParcelableGroupUtils.from(listLoaded[i], accountKey, i, isMember(list)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onStartLoading() {
|
||||||
|
forceLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun isMember(list: Group): Boolean {
|
||||||
|
return list.isMember
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,6 @@ import org.mariotaku.twidere.model.AccountDetails
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus
|
import org.mariotaku.twidere.model.ParcelableStatus
|
||||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
||||||
import org.mariotaku.twidere.util.Nullables
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ConversationLoader(
|
class ConversationLoader(
|
||||||
|
@ -55,7 +54,7 @@ class ConversationLoader(
|
||||||
private var canLoadAllReplies: Boolean = false
|
private var canLoadAllReplies: Boolean = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.status = Nullables.assertNonNull(ParcelUtils.clone(status))
|
this.status = ParcelUtils.clone(status)
|
||||||
ParcelableStatusUtils.makeOriginalStatus(this.status)
|
ParcelableStatusUtils.makeOriginalStatus(this.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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.loader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
import org.mariotaku.microblog.library.MicroBlog
|
||||||
|
import org.mariotaku.microblog.library.MicroBlogException
|
||||||
|
import org.mariotaku.microblog.library.statusnet.model.Group
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.ResponseList
|
||||||
|
import org.mariotaku.twidere.model.ParcelableGroup
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
|
||||||
|
class UserGroupsLoader(
|
||||||
|
context: Context,
|
||||||
|
accountKey: UserKey,
|
||||||
|
private val userKey: UserKey?,
|
||||||
|
private val screenName: String?,
|
||||||
|
data: List<ParcelableGroup>?
|
||||||
|
) : BaseGroupsLoader(context, accountKey, 0, data) {
|
||||||
|
|
||||||
|
@Throws(MicroBlogException::class)
|
||||||
|
override fun getGroups(twitter: MicroBlog): ResponseList<Group> {
|
||||||
|
if (userKey != null) {
|
||||||
|
return twitter.getGroups(userKey.id)
|
||||||
|
} else if (screenName != null) {
|
||||||
|
return twitter.getGroups(screenName)
|
||||||
|
}
|
||||||
|
throw MicroBlogException("No user argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isMember(list: Group): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ class ItemCounts(counts: Int) {
|
||||||
|
|
||||||
fun getItemCountIndex(itemPosition: Int): Int {
|
fun getItemCountIndex(itemPosition: Int): Int {
|
||||||
var sum = 0
|
var sum = 0
|
||||||
for (i in data.indices) {
|
data.forEachIndexed { i, num ->
|
||||||
sum += data[i]
|
sum += num
|
||||||
if (itemPosition < sum) {
|
if (itemPosition < sum) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,7 @@ class ItemCounts(counts: Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemStartPosition(countIndex: Int): Int {
|
fun getItemStartPosition(countIndex: Int): Int {
|
||||||
var sum = 0
|
return (0..countIndex - 1).sumBy { data[it] }
|
||||||
for (i in 0..countIndex - 1) {
|
|
||||||
sum += data[i]
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val itemCount: Int get() = data.sum()
|
val itemCount: Int get() = data.sum()
|
||||||
|
@ -34,4 +30,8 @@ class ItemCounts(counts: Int) {
|
||||||
data[countIndex] = value
|
data[countIndex] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun get(countIndex: Int): Int {
|
||||||
|
return data[countIndex]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -137,6 +137,8 @@ class UpdateStatusTask(
|
||||||
} finally {
|
} finally {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
pendingUpdate.deleteAlways.forEach { item -> item.delete(context) }
|
pendingUpdate.deleteAlways.forEach { item -> item.delete(context) }
|
||||||
|
uploader?.unbindService()
|
||||||
|
shortener?.unbindService()
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,8 +82,7 @@ object HttpClientFactory {
|
||||||
val proxyType = prefs.getString(KEY_PROXY_TYPE, null)
|
val proxyType = prefs.getString(KEY_PROXY_TYPE, null)
|
||||||
val proxyHost = prefs.getString(KEY_PROXY_HOST, null)
|
val proxyHost = prefs.getString(KEY_PROXY_HOST, null)
|
||||||
val proxyPort = NumberUtils.toInt(prefs.getString(KEY_PROXY_PORT, null), -1)
|
val proxyPort = NumberUtils.toInt(prefs.getString(KEY_PROXY_PORT, null), -1)
|
||||||
if (!isEmpty(proxyHost) && TwidereMathUtils.inRange(proxyPort, 0, 65535,
|
if (!isEmpty(proxyHost) && proxyPort in (0..65535)) {
|
||||||
TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE)) {
|
|
||||||
val type = getProxyType(proxyType)
|
val type = getProxyType(proxyType)
|
||||||
if (type != Proxy.Type.DIRECT) {
|
if (type != Proxy.Type.DIRECT) {
|
||||||
builder.proxy(Proxy(type, InetSocketAddress.createUnresolved(proxyHost, proxyPort)))
|
builder.proxy(Proxy(type, InetSocketAddress.createUnresolved(proxyHost, proxyPort)))
|
||||||
|
|
|
@ -77,8 +77,8 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
fun displayPreviewImage(view: ImageView, uri: String?) {
|
fun displayPreviewImage(view: ImageView, url: String?) {
|
||||||
imageLoader.displayImage(uri, view, previewDisplayOptions)
|
imageLoader.displayImage(url, view, previewDisplayOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayPreviewImage(view: ImageView, url: String?, loadingHandler: MediaLoadingHandler?) {
|
fun displayPreviewImage(view: ImageView, url: String?, loadingHandler: MediaLoadingHandler?) {
|
||||||
|
@ -150,12 +150,12 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayProfileImage(view: ImageView, url: String) {
|
fun displayProfileImage(view: ImageView, url: String?) {
|
||||||
imageLoader.displayImage(url, view, profileImageDisplayOptions)
|
imageLoader.displayImage(url, view, profileImageDisplayOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadImageSync(uri: String, targetImageSize: ImageSize, options: DisplayImageOptions): Bitmap? {
|
fun loadImageSync(url: String, targetImageSize: ImageSize, options: DisplayImageOptions): Bitmap? {
|
||||||
return imageLoader.loadImageSync(uri, targetImageSize, options)
|
return imageLoader.loadImageSync(url, targetImageSize, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayDashboardProfileImage(view: ImageView, account: AccountDetails, drawableOnLoading: Drawable?) {
|
fun displayDashboardProfileImage(view: ImageView, account: AccountDetails, drawableOnLoading: Drawable?) {
|
||||||
|
@ -168,11 +168,11 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun displayImage(view: ImageView, url: String) {
|
fun displayImage(view: ImageView, url: String?) {
|
||||||
imageLoader.displayImage(url, view)
|
imageLoader.displayImage(url, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun displayProfileImage(view: ImageView, url: String, listener: ImageLoadingListener) {
|
fun displayProfileImage(view: ImageView, url: String?, listener: ImageLoadingListener) {
|
||||||
imageLoader.displayImage(url, view, profileImageDisplayOptions, listener)
|
imageLoader.displayImage(url, view, profileImageDisplayOptions, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
|
||||||
preloadOnWifiOnly = preferences[mediaPreloadOnWifiOnlyKey]
|
preloadOnWifiOnly = preferences[mediaPreloadOnWifiOnlyKey]
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun displayDashboardProfileImage(view: ImageView, url: String, drawableOnLoading: Drawable?) {
|
private fun displayDashboardProfileImage(view: ImageView, url: String?, drawableOnLoading: Drawable?) {
|
||||||
if (drawableOnLoading != null) {
|
if (drawableOnLoading != null) {
|
||||||
val builder = Builder()
|
val builder = Builder()
|
||||||
builder.cloneFrom(dashboardProfileImageDisplayOptions)
|
builder.cloneFrom(dashboardProfileImageDisplayOptions)
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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.graphics.Bitmap
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility methods for Transitions.
|
||||||
|
*/
|
||||||
|
object TransitionUtils {
|
||||||
|
private const val MAX_IMAGE_SIZE = 1024 * 1024
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
|
||||||
|
* coordinates. `matrix` will be modified during the bitmap creation.
|
||||||
|
*
|
||||||
|
* If the bitmap is large, it will be scaled uniformly down to at most 1MB size.
|
||||||
|
*
|
||||||
|
* @param view The view to create a bitmap for.
|
||||||
|
*
|
||||||
|
* @param matrix The matrix converting the view local coordinates to the coordinates that
|
||||||
|
* the bitmap will be displayed in. `matrix` will be modified before
|
||||||
|
* returning.
|
||||||
|
*
|
||||||
|
* @param bounds The bounds of the bitmap in the destination coordinate system (where the
|
||||||
|
* view should be presented. Typically, this is matrix.mapRect(viewBounds);
|
||||||
|
*
|
||||||
|
* @return A bitmap of the given view or null if bounds has no width or height.
|
||||||
|
*/
|
||||||
|
fun createViewBitmap(view: View, matrix: Matrix, bounds: RectF): Bitmap? {
|
||||||
|
if (bounds.isEmpty) return null
|
||||||
|
var bitmapWidth = Math.round(bounds.width())
|
||||||
|
var bitmapHeight = Math.round(bounds.height())
|
||||||
|
val scale = Math.min(1f, MAX_IMAGE_SIZE.toFloat() / (bitmapWidth * bitmapHeight))
|
||||||
|
bitmapWidth *= scale.toInt()
|
||||||
|
bitmapHeight *= scale.toInt()
|
||||||
|
matrix.postTranslate(-bounds.left, -bounds.top)
|
||||||
|
matrix.postScale(scale, scale)
|
||||||
|
val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
|
||||||
|
val canvas = Canvas(bitmap!!)
|
||||||
|
canvas.concat(matrix)
|
||||||
|
view.draw(canvas)
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.mariotaku.twidere.util
|
||||||
|
|
||||||
|
import android.support.annotation.UiThread
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 16/1/23.
|
||||||
|
*/
|
||||||
|
object TwidereViewUtils {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
fun hitView(x: Float, y: Float, view: View): Boolean {
|
||||||
|
val location = IntArray(2)
|
||||||
|
view.getLocationOnScreen(location)
|
||||||
|
return x in (location[0] until location[0] + view.width)
|
||||||
|
&& y in (location[1] until location[1] + view.height)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
* 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.util.content
|
||||||
|
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import android.os.Build
|
||||||
|
import org.mariotaku.kpreferences.get
|
||||||
|
import org.mariotaku.sqliteqb.library.*
|
||||||
|
import org.mariotaku.sqliteqb.library.Columns.Column
|
||||||
|
import org.mariotaku.sqliteqb.library.query.SQLCreateTriggerQuery.Event
|
||||||
|
import org.mariotaku.sqliteqb.library.query.SQLCreateTriggerQuery.Type
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
|
||||||
|
import org.mariotaku.twidere.annotation.CustomTabType
|
||||||
|
import org.mariotaku.twidere.constant.defaultAPIConfigKey
|
||||||
|
import org.mariotaku.twidere.model.Tab
|
||||||
|
import org.mariotaku.twidere.model.TabValuesCreator
|
||||||
|
import org.mariotaku.twidere.model.tab.TabConfiguration
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||||
|
import org.mariotaku.twidere.util.TwidereQueryBuilder.ConversationsEntryQueryBuilder
|
||||||
|
import org.mariotaku.twidere.util.TwidereQueryBuilder.DirectMessagesQueryBuilder
|
||||||
|
import org.mariotaku.twidere.util.content.DatabaseUpgradeHelper.safeUpgrade
|
||||||
|
import org.mariotaku.twidere.util.migrateAccounts
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class TwidereSQLiteOpenHelper(
|
||||||
|
private val context: Context,
|
||||||
|
name: String,
|
||||||
|
version: Int
|
||||||
|
) : SQLiteOpenHelper(context, name, null, version) {
|
||||||
|
|
||||||
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
|
db.beginTransaction()
|
||||||
|
db.execSQL(createTable(Statuses.TABLE_NAME, Statuses.COLUMNS, Statuses.TYPES, true))
|
||||||
|
db.execSQL(createTable(Activities.AboutMe.TABLE_NAME, Activities.AboutMe.COLUMNS, Activities.AboutMe.TYPES, true))
|
||||||
|
db.execSQL(createTable(Activities.ByFriends.TABLE_NAME, Activities.ByFriends.COLUMNS, Activities.ByFriends.TYPES, true))
|
||||||
|
db.execSQL(createTable(Drafts.TABLE_NAME, Drafts.COLUMNS, Drafts.TYPES, true))
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
|
||||||
|
db.beginTransaction()
|
||||||
|
db.execSQL(createTable(CachedUsers.TABLE_NAME, CachedUsers.COLUMNS, CachedUsers.TYPES, true,
|
||||||
|
createConflictReplaceConstraint(CachedUsers.USER_KEY)))
|
||||||
|
db.execSQL(createTable(CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES, true))
|
||||||
|
db.execSQL(createTable(CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS, CachedTrends.Local.TYPES,
|
||||||
|
true))
|
||||||
|
db.execSQL(createTable(CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES, true))
|
||||||
|
db.execSQL(createTable(CachedRelationships.TABLE_NAME, CachedRelationships.COLUMNS, CachedRelationships.TYPES, true,
|
||||||
|
createConflictReplaceConstraint(CachedRelationships.ACCOUNT_KEY, CachedRelationships.USER_KEY)))
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
|
||||||
|
|
||||||
|
db.beginTransaction()
|
||||||
|
db.execSQL(createTable(Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES, true))
|
||||||
|
db.execSQL(createTable(Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES, true))
|
||||||
|
db.execSQL(createTable(Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES, true))
|
||||||
|
db.execSQL(createTable(Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES, true))
|
||||||
|
db.execSQL(createTable(Filters.Subscriptions.TABLE_NAME, Filters.Subscriptions.COLUMNS, Filters.Subscriptions.TYPES, true))
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
|
||||||
|
db.beginTransaction()
|
||||||
|
db.execSQL(createTable(DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS,
|
||||||
|
DirectMessages.Inbox.TYPES, true))
|
||||||
|
db.execSQL(createTable(DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS,
|
||||||
|
DirectMessages.Outbox.TYPES, true))
|
||||||
|
db.execSQL(createTable(Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, true))
|
||||||
|
db.execSQL(createTable(SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true))
|
||||||
|
db.execSQL(createTable(SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true))
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
|
||||||
|
db.beginTransaction()
|
||||||
|
createViews(db)
|
||||||
|
createTriggers(db)
|
||||||
|
createIndices(db)
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
|
||||||
|
setupDefaultTabs(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupDefaultTabs(db: SQLiteDatabase) {
|
||||||
|
db.beginTransaction()
|
||||||
|
@CustomTabType
|
||||||
|
val tabTypes = arrayOf(CustomTabType.HOME_TIMELINE, CustomTabType.NOTIFICATIONS_TIMELINE,
|
||||||
|
CustomTabType.TRENDS_SUGGESTIONS, CustomTabType.DIRECT_MESSAGES)
|
||||||
|
for (i in 0 until tabTypes.size) {
|
||||||
|
@CustomTabType
|
||||||
|
val tabType = tabTypes[i]
|
||||||
|
val conf = TabConfiguration.ofType(tabType)
|
||||||
|
val tab = Tab().apply {
|
||||||
|
this.type = tabType
|
||||||
|
this.icon = conf!!.icon.persistentKey
|
||||||
|
this.position = i
|
||||||
|
}
|
||||||
|
db.insert(Tabs.TABLE_NAME, null, TabValuesCreator.create(tab))
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createConflictReplaceConstraint(vararg columns: String): Constraint {
|
||||||
|
return Constraint.unique(Columns(*columns), OnConflict.IGNORE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createIndices(db: SQLiteDatabase) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return
|
||||||
|
db.execSQL(createIndex("statuses_index", Statuses.TABLE_NAME, arrayOf(Statuses.ACCOUNT_KEY), true))
|
||||||
|
db.execSQL(createIndex("messages_inbox_index", DirectMessages.Inbox.TABLE_NAME, arrayOf(DirectMessages.ACCOUNT_KEY), true))
|
||||||
|
db.execSQL(createIndex("messages_outbox_index", DirectMessages.Outbox.TABLE_NAME, arrayOf(DirectMessages.ACCOUNT_KEY), true))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createViews(db: SQLiteDatabase) {
|
||||||
|
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.TABLE_NAME).sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropView(true, DirectMessages.ConversationEntries.TABLE_NAME).sql)
|
||||||
|
|
||||||
|
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.TABLE_NAME)
|
||||||
|
.`as`(DirectMessagesQueryBuilder.build()).buildSQL())
|
||||||
|
db.execSQL(SQLQueryBuilder.createView(true, DirectMessages.ConversationEntries.TABLE_NAME)
|
||||||
|
.`as`(ConversationsEntryQueryBuilder.build()).buildSQL())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTriggers(db: SQLiteDatabase) {
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_statuses").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_cached_statuses").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_received_messages").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_sent_messages").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "on_user_cache_update_trigger").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTrigger(true, "delete_old_cached_hashtags").sql)
|
||||||
|
db.execSQL(createDeleteDuplicateStatusTrigger("delete_old_statuses", Statuses.TABLE_NAME).sql)
|
||||||
|
db.execSQL(createDeleteDuplicateStatusTrigger("delete_old_cached_statuses", CachedStatuses.TABLE_NAME).sql)
|
||||||
|
db.execSQL(createDeleteDuplicateMessageTrigger("delete_old_received_messages", DirectMessages.Inbox.TABLE_NAME).sql)
|
||||||
|
db.execSQL(createDeleteDuplicateMessageTrigger("delete_old_sent_messages", DirectMessages.Outbox.TABLE_NAME).sql)
|
||||||
|
|
||||||
|
// Update user info in filtered users
|
||||||
|
val cachedUsersTable = Table(CachedUsers.TABLE_NAME)
|
||||||
|
val filteredUsersTable = Table(Filters.Users.TABLE_NAME)
|
||||||
|
db.execSQL(SQLQueryBuilder.createTrigger(false, true, "on_user_cache_update_trigger")
|
||||||
|
.type(Type.BEFORE)
|
||||||
|
.event(Event.INSERT)
|
||||||
|
.on(cachedUsersTable)
|
||||||
|
.forEachRow(true)
|
||||||
|
.actions(SQLQueryBuilder.update(OnConflict.REPLACE, filteredUsersTable)
|
||||||
|
.set(SetValue(Column(Filters.Users.NAME), Column(Table.NEW, CachedUsers.NAME)),
|
||||||
|
SetValue(Column(Filters.Users.SCREEN_NAME), Column(Table.NEW, CachedUsers.SCREEN_NAME)))
|
||||||
|
.where(Expression.equals(Column(Filters.Users.USER_KEY), Column(Table.NEW, CachedUsers.USER_KEY)))
|
||||||
|
.build())
|
||||||
|
.buildSQL())
|
||||||
|
|
||||||
|
// Delete duplicated hashtags ignoring case
|
||||||
|
val cachedHashtagsTable = Table(CachedHashtags.TABLE_NAME)
|
||||||
|
db.execSQL(SQLQueryBuilder.createTrigger(false, true, "delete_old_cached_hashtags")
|
||||||
|
.type(Type.BEFORE)
|
||||||
|
.event(Event.INSERT)
|
||||||
|
.on(cachedHashtagsTable)
|
||||||
|
.forEachRow(true)
|
||||||
|
.actions(SQLQueryBuilder.deleteFrom(cachedHashtagsTable)
|
||||||
|
.where(Expression.like(Column(CachedHashtags.NAME), Column(Table.NEW, CachedHashtags.NAME)))
|
||||||
|
.build())
|
||||||
|
.buildSQL())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDeleteDuplicateStatusTrigger(triggerName: String, tableName: String): SQLQuery {
|
||||||
|
val table = Table(tableName)
|
||||||
|
val deleteOld = SQLQueryBuilder.deleteFrom(table).where(Expression.and(
|
||||||
|
Expression.equals(Column(Statuses.ACCOUNT_KEY), Column(Table.NEW, Statuses.ACCOUNT_KEY)),
|
||||||
|
Expression.equals(Column(Statuses.STATUS_ID), Column(Table.NEW, Statuses.STATUS_ID))
|
||||||
|
)).build()
|
||||||
|
return SQLQueryBuilder.createTrigger(false, true, triggerName)
|
||||||
|
.type(Type.BEFORE).event(Event.INSERT).on(table).forEachRow(true)
|
||||||
|
.actions(deleteOld).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createDeleteDuplicateMessageTrigger(triggerName: String, tableName: String): SQLQuery {
|
||||||
|
val table = Table(tableName)
|
||||||
|
val deleteOld = SQLQueryBuilder.deleteFrom(table).where(Expression.and(
|
||||||
|
Expression.equals(Column(DirectMessages.ACCOUNT_KEY), Column(Table.NEW, DirectMessages.ACCOUNT_KEY)),
|
||||||
|
Expression.equals(Column(DirectMessages.MESSAGE_ID), Column(Table.NEW, DirectMessages.MESSAGE_ID))
|
||||||
|
)).build()
|
||||||
|
return SQLQueryBuilder.createTrigger(false, true, triggerName)
|
||||||
|
.type(Type.BEFORE).event(Event.INSERT).on(table).forEachRow(true)
|
||||||
|
.actions(deleteOld).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
handleVersionChange(db, oldVersion, newVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
handleVersionChange(db, oldVersion, newVersion)
|
||||||
|
if (oldVersion <= 43 && newVersion >= 44 && newVersion <= 153) {
|
||||||
|
val values = ContentValues()
|
||||||
|
val prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||||
|
// Here I use old consumer key/secret because it's default key for
|
||||||
|
// older versions
|
||||||
|
val defaultAPIConfig = prefs[defaultAPIConfigKey]
|
||||||
|
values.put(Accounts.CONSUMER_KEY, defaultAPIConfig.consumerKey)
|
||||||
|
values.put(Accounts.CONSUMER_SECRET, defaultAPIConfig.consumerSecret)
|
||||||
|
db.update(Accounts.TABLE_NAME, values, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleVersionChange(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
|
||||||
|
if (oldVersion <= 153) {
|
||||||
|
migrateLegacyAccounts(db)
|
||||||
|
if (newVersion > 153) {
|
||||||
|
migrateAccounts(AccountManager.get(context), db)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTable(true, Accounts.TABLE_NAME).sql)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safeUpgrade(db, Statuses.TABLE_NAME, Statuses.COLUMNS, Statuses.TYPES, true, null)
|
||||||
|
safeUpgrade(db, Activities.AboutMe.TABLE_NAME, Activities.AboutMe.COLUMNS,
|
||||||
|
Activities.AboutMe.TYPES, true, null)
|
||||||
|
safeUpgrade(db, Activities.ByFriends.TABLE_NAME, Activities.ByFriends.COLUMNS,
|
||||||
|
Activities.ByFriends.TYPES, true, null)
|
||||||
|
migrateDrafts(db)
|
||||||
|
safeUpgrade(db, CachedUsers.TABLE_NAME, CachedUsers.COLUMNS, CachedUsers.TYPES, true, null,
|
||||||
|
createConflictReplaceConstraint(CachedUsers.USER_KEY))
|
||||||
|
safeUpgrade(db, CachedStatuses.TABLE_NAME, CachedStatuses.COLUMNS, CachedStatuses.TYPES, true, null)
|
||||||
|
safeUpgrade(db, CachedHashtags.TABLE_NAME, CachedHashtags.COLUMNS, CachedHashtags.TYPES, true, null)
|
||||||
|
safeUpgrade(db, CachedRelationships.TABLE_NAME, CachedRelationships.COLUMNS, CachedRelationships.TYPES, true, null,
|
||||||
|
createConflictReplaceConstraint(CachedRelationships.ACCOUNT_KEY, CachedRelationships.USER_KEY))
|
||||||
|
|
||||||
|
migrateFilters(db, oldVersion)
|
||||||
|
safeUpgrade(db, DirectMessages.Inbox.TABLE_NAME, DirectMessages.Inbox.COLUMNS,
|
||||||
|
DirectMessages.Inbox.TYPES, true, null)
|
||||||
|
safeUpgrade(db, DirectMessages.Outbox.TABLE_NAME, DirectMessages.Outbox.COLUMNS,
|
||||||
|
DirectMessages.Outbox.TYPES, true, null)
|
||||||
|
safeUpgrade(db, CachedTrends.Local.TABLE_NAME, CachedTrends.Local.COLUMNS,
|
||||||
|
CachedTrends.Local.TYPES, true, null)
|
||||||
|
safeUpgrade(db, Tabs.TABLE_NAME, Tabs.COLUMNS, Tabs.TYPES, false, null)
|
||||||
|
safeUpgrade(db, SavedSearches.TABLE_NAME, SavedSearches.COLUMNS, SavedSearches.TYPES, true, null)
|
||||||
|
safeUpgrade(db, SearchHistory.TABLE_NAME, SearchHistory.COLUMNS, SearchHistory.TYPES, true, null)
|
||||||
|
|
||||||
|
if (oldVersion < 131) {
|
||||||
|
migrateFilteredUsers(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.beginTransaction()
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTable(true, "network_usages").sql)
|
||||||
|
db.execSQL(SQLQueryBuilder.dropTable(true, "mentions").sql)
|
||||||
|
createViews(db)
|
||||||
|
createTriggers(db)
|
||||||
|
createIndices(db)
|
||||||
|
db.setTransactionSuccessful()
|
||||||
|
db.endTransaction()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateDrafts(db: SQLiteDatabase) {
|
||||||
|
val draftsAlias = HashMap<String, String>()
|
||||||
|
draftsAlias.put(Drafts.MEDIA, "medias")
|
||||||
|
safeUpgrade(db, Drafts.TABLE_NAME, Drafts.COLUMNS, Drafts.TYPES, false, draftsAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateFilters(db: SQLiteDatabase, oldVersion: Int) {
|
||||||
|
safeUpgrade(db, Filters.Users.TABLE_NAME, Filters.Users.COLUMNS, Filters.Users.TYPES,
|
||||||
|
oldVersion < 49, null)
|
||||||
|
|
||||||
|
val filtersAlias = HashMap<String, String>()
|
||||||
|
safeUpgrade(db, Filters.Keywords.TABLE_NAME, Filters.Keywords.COLUMNS, Filters.Keywords.TYPES,
|
||||||
|
oldVersion < 49, filtersAlias)
|
||||||
|
safeUpgrade(db, Filters.Sources.TABLE_NAME, Filters.Sources.COLUMNS, Filters.Sources.TYPES,
|
||||||
|
oldVersion < 49, filtersAlias)
|
||||||
|
safeUpgrade(db, Filters.Links.TABLE_NAME, Filters.Links.COLUMNS, Filters.Links.TYPES,
|
||||||
|
oldVersion < 49, filtersAlias)
|
||||||
|
safeUpgrade(db, Filters.Subscriptions.TABLE_NAME, Filters.Subscriptions.COLUMNS,
|
||||||
|
Filters.Subscriptions.TYPES, false, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateLegacyAccounts(db: SQLiteDatabase) {
|
||||||
|
val accountsAlias = HashMap<String, String>()
|
||||||
|
accountsAlias.put(Accounts.SCREEN_NAME, "username")
|
||||||
|
accountsAlias.put(Accounts.NAME, "username")
|
||||||
|
accountsAlias.put(Accounts.ACCOUNT_KEY, "user_id")
|
||||||
|
accountsAlias.put(Accounts.COLOR, "user_color")
|
||||||
|
accountsAlias.put(Accounts.OAUTH_TOKEN_SECRET, "token_secret")
|
||||||
|
accountsAlias.put(Accounts.API_URL_FORMAT, "rest_base_url")
|
||||||
|
safeUpgrade(db, Accounts.TABLE_NAME, Accounts.COLUMNS, Accounts.TYPES, false, accountsAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateFilteredUsers(db: SQLiteDatabase) {
|
||||||
|
db.execSQL(SQLQueryBuilder.update(OnConflict.REPLACE, Filters.Users.TABLE_NAME)
|
||||||
|
.set(SetValue(Filters.Users.USER_KEY, RawSQLLang(Filters.Users.USER_KEY + "||?")))
|
||||||
|
.where(Expression.notLikeArgs(Column(Filters.Users.USER_KEY)))
|
||||||
|
.buildSQL(),
|
||||||
|
arrayOf<Any>("@twitter.com", "%@%"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTable(tableName: String, columns: Array<String>, types: Array<String>,
|
||||||
|
createIfNotExists: Boolean, vararg constraints: Constraint): String {
|
||||||
|
val qb = SQLQueryBuilder.createTable(createIfNotExists, tableName)
|
||||||
|
qb.columns(*NewColumn.createNewColumns(columns, types))
|
||||||
|
qb.constraint(*constraints)
|
||||||
|
return qb.buildSQL()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createIndex(indexName: String, tableName: String, columns: Array<String>,
|
||||||
|
createIfNotExists: Boolean): String {
|
||||||
|
val qb = SQLQueryBuilder.createIndex(false, createIfNotExists)
|
||||||
|
qb.name(indexName)
|
||||||
|
qb.on(Table(tableName), Columns(*columns))
|
||||||
|
return qb.buildSQL()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import org.mariotaku.restfu.http.RestHttpClient
|
||||||
import org.mariotaku.twidere.BuildConfig
|
import org.mariotaku.twidere.BuildConfig
|
||||||
import org.mariotaku.twidere.Constants
|
import org.mariotaku.twidere.Constants
|
||||||
import org.mariotaku.twidere.constant.SharedPreferenceConstants
|
import org.mariotaku.twidere.constant.SharedPreferenceConstants
|
||||||
|
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CACHE_SIZE_LIMIT
|
||||||
import org.mariotaku.twidere.model.DefaultFeatures
|
import org.mariotaku.twidere.model.DefaultFeatures
|
||||||
import org.mariotaku.twidere.util.*
|
import org.mariotaku.twidere.util.*
|
||||||
import org.mariotaku.twidere.util.imageloader.ReadOnlyDiskLRUNameCache
|
import org.mariotaku.twidere.util.imageloader.ReadOnlyDiskLRUNameCache
|
||||||
|
@ -310,7 +311,7 @@ class ApplicationModule(private val application: Application) {
|
||||||
val cacheDir = Utils.getExternalCacheDir(application, dirName)
|
val cacheDir = Utils.getExternalCacheDir(application, dirName)
|
||||||
val fallbackCacheDir = Utils.getInternalCacheDir(application, dirName)
|
val fallbackCacheDir = Utils.getInternalCacheDir(application, dirName)
|
||||||
val fileNameGenerator = URLFileNameGenerator()
|
val fileNameGenerator = URLFileNameGenerator()
|
||||||
val cacheSize = TwidereMathUtils.clamp(preferences.getInt(SharedPreferenceConstants.KEY_CACHE_SIZE_LIMIT, 300), 100, 500)
|
val cacheSize = preferences.getInt(KEY_CACHE_SIZE_LIMIT, 300).coerceIn(100..500)
|
||||||
try {
|
try {
|
||||||
val cacheMaxSizeBytes = cacheSize * 1024 * 1024
|
val cacheMaxSizeBytes = cacheSize * 1024 * 1024
|
||||||
if (cacheDir != null)
|
if (cacheDir != null)
|
||||||
|
|
|
@ -17,25 +17,19 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.mariotaku.twidere.util.view;
|
package org.mariotaku.twidere.util.view
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import com.rengwuxian.materialedittext.validation.METValidator
|
||||||
|
|
||||||
import com.rengwuxian.materialedittext.validation.METValidator;
|
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||||
|
|
||||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/9/3.
|
* Created by mariotaku on 15/9/3.
|
||||||
*/
|
*/
|
||||||
public class ConsumerKeySecretValidator extends METValidator {
|
class ConsumerKeySecretValidator(errorMessage: String) : METValidator(errorMessage) {
|
||||||
public ConsumerKeySecretValidator(String errorMessage) {
|
|
||||||
super(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
override fun isValid(text: CharSequence, isEmpty: Boolean): Boolean {
|
||||||
public boolean isValid(@NonNull CharSequence text, boolean isEmpty) {
|
return MicroBlogAPIFactory.isValidConsumerKeySecret(text)
|
||||||
return MicroBlogAPIFactory.isValidConsumerKeySecret(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewConfiguration
|
import android.view.ViewConfiguration
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import org.mariotaku.ktextension.coerceInOr
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 2017/1/29.
|
* Created by mariotaku on 2017/1/29.
|
||||||
|
@ -34,7 +35,7 @@ class MediaSwipeCloseContainer(context: Context, attrs: AttributeSet? = null) :
|
||||||
|
|
||||||
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
|
override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
|
||||||
val container = this@MediaSwipeCloseContainer
|
val container = this@MediaSwipeCloseContainer
|
||||||
return top.coerceIn(-container.height, container.height)
|
return top.coerceInOr(-container.height..container.height, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getViewVerticalDragRange(child: View?): Int {
|
override fun getViewVerticalDragRange(child: View?): Int {
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
android:paddingLeft="@dimen/element_spacing_normal"
|
android:paddingLeft="@dimen/element_spacing_normal"
|
||||||
android:paddingRight="0dp"
|
android:paddingRight="0dp"
|
||||||
android:paddingStart="@dimen/element_spacing_normal"
|
android:paddingStart="@dimen/element_spacing_normal"
|
||||||
tools:showIn="@layout/layout_api_editor">
|
tools:showIn="@layout/dialog_api_editor">
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/editSameOAuthSigningUrl"
|
android:id="@+id/editSameOAuthSigningUrl"
|
||||||
|
|
Loading…
Reference in New Issue