code cleanup

This commit is contained in:
Mariotaku Lee 2017-02-04 18:42:14 +08:00
parent a4f3fd94f8
commit 8cddbb0dfc
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
91 changed files with 711 additions and 3080 deletions

View File

@ -10,6 +10,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
/**
* Created by mariotaku on 16/3/11.
*/
@SuppressWarnings("RedundantThrows")
public interface BlocksResources {
@POST("/blocks/create.json")

View File

@ -8,6 +8,7 @@ import org.mariotaku.microblog.library.twitter.model.DirectMessage;
/**
* Created by mariotaku on 16/3/31.
*/
@SuppressWarnings("RedundantThrows")
public interface DirectMessagesResources {
@POST("/direct_messages/new.json")

View File

@ -8,6 +8,7 @@ import org.mariotaku.microblog.library.twitter.model.Status;
/**
* Created by mariotaku on 16/3/11.
*/
@SuppressWarnings("RedundantThrows")
public interface FavoritesResources {
@POST("/favorites/create/{id}.json")

View File

@ -12,6 +12,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
/**
* Created by mariotaku on 16/3/11.
*/
@SuppressWarnings("RedundantThrows")
public interface FriendshipsResources {
@POST("/friendships/create.json")

View File

@ -13,6 +13,7 @@ import org.mariotaku.microblog.library.twitter.model.User;
/**
* Created by mariotaku on 16/3/4.
*/
@SuppressWarnings("RedundantThrows")
public interface GroupResources {
@GET("/statusnet/groups/timeline/{group_id}.json")

View File

@ -35,6 +35,7 @@ import org.mariotaku.restfu.annotation.param.Param;
import org.mariotaku.restfu.annotation.param.Queries;
import org.mariotaku.restfu.annotation.param.Query;
@SuppressWarnings("RedundantThrows")
public interface ListResources {
@POST("/lists/members/create.json")
UserList addUserListMember(@Query("list_id") String listId, @Query("user_id") String userId) throws MicroBlogException;

View File

@ -29,8 +29,10 @@ import java.util.ArrayList;
import java.util.List;
/**
* Mapper for IDs object
* Created by mariotaku on 15/10/21.
*/
@SuppressWarnings("unused")
public class IDs$$JsonObjectMapper extends JsonMapper<IDs> {
@SuppressWarnings("TryWithIdenticalCatches")

View File

@ -27,9 +27,9 @@ import org.mariotaku.twidere.util.model.AccountDetailsUtils;
import java.io.IOException;
/**
* Object holding account info and credentials
* Created by mariotaku on 2016/12/3.
*/
@ParcelablePlease
@JsonObject
public class AccountDetails implements Parcelable, Comparable<AccountDetails> {

View File

@ -41,7 +41,6 @@ import org.mariotaku.twidere.model.util.UserKeyCursorFieldConverter;
import org.mariotaku.twidere.provider.TwidereDataStore;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@ -445,11 +444,11 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
}
@OnJsonParseComplete
void onParseComplete() throws IOException {
void onParseComplete() {
fixSortId();
}
void fixSortId() {
private void fixSortId() {
if (sort_id <= 0) {
try {
sort_id = Long.parseLong(id);

View File

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

View File

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

View File

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

View File

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

View File

@ -29,10 +29,6 @@ public final class ParseUtils implements TwidereConstants {
private ParseUtils() {
}
public static String parseString(final String object) {
return object;
}
public static String parseString(final Object object) {
return parseString(object, null);
}

View File

@ -35,5 +35,6 @@ android {
}
dependencies {
compile "com.android.support:support-compat:$android_support_lib_version"
compile fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@ -33,6 +33,7 @@ import android.graphics.Shader.TileMode;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import java.util.ArrayList;
import java.util.Calendar;
@ -77,9 +78,9 @@ public class NyanDrawingHelper {
} else {
sakamotoRes = R.drawable.nyan_sakamoto;
}
mSakamotoHelper = new DrawableDrawingHelper(mResources.getDrawable(sakamotoRes));
mSakamotoHelper = new DrawableDrawingHelper(ContextCompat.getDrawable(context, sakamotoRes));
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) {

View File

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

View File

@ -56,6 +56,7 @@ public final class CRLFLineReader extends BufferedReader
StringBuilder sb = new StringBuilder();
int intch;
boolean prevWasCR = false;
//noinspection SynchronizeOnNonFinalField
synchronized(lock) { // make thread-safe (hopefully!)
while((intch = read()) != -1)
{

View File

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

View File

@ -51,7 +51,6 @@ import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_TITLE;
public final class DataExportImportTypeSelectorDialogFragment extends BaseDialogFragment implements
OnMultiChoiceClickListener, OnClickListener, OnShowListener, OnItemClickListener {
private TypeAdapter mAdapter;
private ListView mListView;
@Override
@ -84,20 +83,20 @@ public final class DataExportImportTypeSelectorDialogFragment extends BaseDialog
public final Dialog onCreateDialog(final Bundle savedInstanceState) {
final Context context = getActivity();
final int flags = getEnabledFlags();
mAdapter = new TypeAdapter(context, flags);
mListView = new ListView(context);
mAdapter.add(new Type(R.string.settings, DataImportExportUtils.FLAG_PREFERENCES));
mAdapter.add(new Type(R.string.title_nicknames, DataImportExportUtils.FLAG_NICKNAMES));
mAdapter.add(new Type(R.string.title_user_colors, DataImportExportUtils.FLAG_USER_COLORS));
mAdapter.add(new Type(R.string.custom_host_mapping, DataImportExportUtils.FLAG_HOST_MAPPING));
mAdapter.add(new Type(R.string.keyboard_shortcuts, DataImportExportUtils.FLAG_KEYBOARD_SHORTCUTS));
mAdapter.add(new Type(R.string.title_filters, DataImportExportUtils.FLAG_FILTERS));
mAdapter.add(new Type(R.string.tabs, DataImportExportUtils.FLAG_TABS));
mListView.setAdapter(mAdapter);
final TypeAdapter adapter = new TypeAdapter(context, flags);
adapter.add(new Type(R.string.settings, DataImportExportUtils.FLAG_PREFERENCES));
adapter.add(new Type(R.string.title_nicknames, DataImportExportUtils.FLAG_NICKNAMES));
adapter.add(new Type(R.string.title_user_colors, DataImportExportUtils.FLAG_USER_COLORS));
adapter.add(new Type(R.string.custom_host_mapping, DataImportExportUtils.FLAG_HOST_MAPPING));
adapter.add(new Type(R.string.keyboard_shortcuts, DataImportExportUtils.FLAG_KEYBOARD_SHORTCUTS));
adapter.add(new Type(R.string.title_filters, DataImportExportUtils.FLAG_FILTERS));
adapter.add(new Type(R.string.tabs, DataImportExportUtils.FLAG_TABS));
mListView.setAdapter(adapter);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setOnItemClickListener(this);
for (int i = 0, j = mAdapter.getCount(); i < j; i++) {
mListView.setItemChecked(i, mAdapter.isEnabled(i));
for (int i = 0, j = adapter.getCount(); i < j; i++) {
mListView.setItemChecked(i, adapter.isEnabled(i));
}
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(getTitle());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.http.HttpRequest;
import org.mariotaku.restfu.http.HttpResponse;
import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.twidere.util.Utils;
import java.io.IOException;
@ -70,7 +71,7 @@ public class DefaultFeatures {
jsonParser.skipChildren();
}
} finally {
response.close();
Utils.closeSilently(response);
}
return true;
}

View File

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

View File

@ -19,14 +19,17 @@
package org.mariotaku.twidere.model.message;
import org.mariotaku.twidere.model.UserKey;
/**
* Called when account changed
* Created by mariotaku on 15/4/24.
*/
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) {
this.account_ids = account_ids;
this.activated_ids = activated_ids;
public AccountChangedEvent(UserKey[] account_keys, UserKey[] activated_keys) {
this.account_keys = account_keys;
this.activated_keys = activated_keys;
}
}

View File

@ -30,7 +30,7 @@ public class GetMessagesTaskEvent {
@NonNull
public final Uri uri;
public final boolean running;
private final Exception exception;
public final Exception exception;
public GetMessagesTaskEvent(@NonNull Uri uri, boolean running, Exception exception) {
this.uri = uri;

View File

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

View File

@ -3,9 +3,9 @@ package org.mariotaku.twidere.model.tab.iface;
import org.mariotaku.twidere.model.AccountDetails;
/**
* Account callback for extra configurations
* Created by mariotaku on 2016/11/30.
*/
public interface AccountCallback {
AccountDetails getAccount();

View File

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

View File

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

View File

@ -106,6 +106,7 @@ public abstract class AbsServiceInterface<I extends IInterface> implements IInte
}
public interface CheckServiceAction {
@SuppressWarnings("RedundantThrows")
void check(@Nullable Bundle metaData) throws CheckServiceException;
}

View File

@ -513,6 +513,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
public SingleResponse<Relationship> doLongOperation(Object param) {
final MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountKey);
try {
if (microBlog == null) {
throw new MicroBlogException("No account");
}
final Relationship relationship = microBlog.updateFriendship(userKey.getId(), update);
if (!relationship.isSourceWantRetweetsFromTarget()) {
// TODO remove cached retweets
@ -555,6 +558,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
MicroBlog microBlog = MicroBlogAPIFactory.getInstance(context, accountId);
if (!Utils.isOfficialCredentials(context, accountId)) continue;
try {
if (microBlog == null) {
throw new MicroBlogException("No account");
}
microBlog.setActivitiesAboutMeUnread(cursor);
} catch (MicroBlogException e) {
DebugLog.w(LOGTAG, null, e);
@ -733,11 +739,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
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.
//TODO insert to blocked users data
final ContentValues values = new ContentValues();

View File

@ -98,11 +98,7 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static android.text.TextUtils.isEmpty;
/**
* 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};
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 {
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) {
final String cached = sAccountNames.get(accountKey);
if (!isEmpty(cached)) return cached;
AccountManager am = AccountManager.get(context);
Account account = AccountUtils.findByAccountKey(am, accountKey);
if (account == null) return null;
@ -350,13 +341,9 @@ public class DataStoreUtils implements Constants {
public static String getAccountScreenName(final Context context, final UserKey accountKey) {
if (context == null) return null;
final String cached = sAccountScreenNames.get(accountKey);
if (!isEmpty(cached)) return cached;
AccountManager am = AccountManager.get(context);
Account account = AccountUtils.findByAccountKey(am, accountKey);
if (account == null) return null;
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) {
return isFilteringUser(context, userKey.toString());
}

View File

@ -42,11 +42,6 @@ public class HtmlEscapeHelper {
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) {
if (string == null) return null;
return unescape(string.replace("<br/>", "\n").replaceAll("<!--.*?-->|<[^>]+>", ""));

View File

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

View File

@ -1,16 +1,6 @@
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.Set;
/**
* Created by mariotaku on 16/3/8.
@ -19,46 +9,6 @@ public class 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) {
String result = String.format(Locale.US, "%." + decimalDigits + "f", num);
int dotIdx = result.lastIndexOf('.');

View File

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

View File

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

View File

@ -37,10 +37,6 @@ public final class 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,
final ServiceConnection callback) {

View File

@ -24,7 +24,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff.Mode;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
@ -165,10 +164,6 @@ public class ThemeUtils implements Constants {
return TwidereColorUtils.YIQToColor(Color.alpha(accentColor), yiq);
}
public static Resources getResources(final Context context) {
return context.getResources();
}
public static Drawable getSelectableItemBackgroundDrawable(final Context context) {
return getDrawableFromThemeAttribute(context, android.R.attr.selectableItemBackground);
}
@ -304,15 +299,6 @@ public class ThemeUtils implements Constants {
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) {
final TypedArray a = context.obtainStyledAttributes(new int[]{android.R.attr.windowBackground});
try {

View File

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

View File

@ -20,14 +20,10 @@
package org.mariotaku.twidere.util;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class TwidereArrayUtils {
@ -43,15 +39,6 @@ public final class TwidereArrayUtils {
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) {
if (array1 == null || array2 == null) return array1 == array2;
if (array1.length != array2.length) return false;
@ -61,40 +48,6 @@ public final class TwidereArrayUtils {
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) {
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) {
final StringBuilder builder = new StringBuilder();
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) {
for (int i = 0; i < array.length; i++) {
array[i] += offset;

View File

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

View File

@ -117,7 +117,4 @@ public class TwidereColorUtils {
return (yiq >= threshold) ? colorDark : colorLight;
}
public static int getYIQContrast(int color1, int color2) {
return getYIQLuminance(color1) - getYIQLuminance(color2);
}
}

View File

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

View File

@ -19,24 +19,11 @@
package org.mariotaku.twidere.util;
import android.support.annotation.NonNull;
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() {
}
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) {
final int max = Math.max(bound1, bound2), min = Math.min(bound1, bound2);
return Math.max(Math.min(num, max), min);
@ -57,43 +44,4 @@ public class TwidereMathUtils {
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);
}
}

View File

@ -52,7 +52,6 @@ public class TwidereStringUtils {
/**
* Fix to https://github.com/TwidereProject/Twidere-Android/issues/449
* @param string
*/
public static void fixSHY(Spannable string) {
for (int i = 0, j = string.length(); i < j; i++) {

View File

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

View File

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

View File

@ -48,10 +48,8 @@ public class UserColorNameManager {
private final SharedPreferences colorPreferences, nicknamePreferences;
private final LruCache<String, Integer> colorCache;
private final LruCache<String, String> nicknameCache;
private final Context context;
public UserColorNameManager(Context context) {
this.context = context;
colorPreferences = context.getSharedPreferences(USER_COLOR_PREFERENCES_NAME, Context.MODE_PRIVATE);
nicknamePreferences = context.getSharedPreferences(USER_NICKNAME_PREFERENCES_NAME, Context.MODE_PRIVATE);
colorCache = new LruCache<>(512);

View File

@ -21,7 +21,6 @@ package org.mariotaku.twidere.util;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.ContentResolver;
@ -43,7 +42,6 @@ import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
@ -51,8 +49,6 @@ import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
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.v7.app.AppCompatActivity;
import android.support.v7.view.menu.MenuBuilder;
import android.system.ErrnoException;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
@ -76,13 +69,9 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.Toast;
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.ConversationEntries;
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.ShapedImageView;
import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle;
import org.mariotaku.twidere.view.TabPagerIndicator;
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,
@Nullable final CharSequence[] links,
@Nullable final CharSequence text) {
@ -626,20 +585,6 @@ public final class Utils implements Constants {
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) {
final NumberFormat nf = NumberFormat.getInstance(locale);
return nf.format(number);
@ -692,14 +637,6 @@ public final class Utils implements Constants {
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
public static int getMediaPreviewStyle(String 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;
}
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) {
if (uri == null) return UriMatcher.NO_MATCH;
return HOME_TABS_URI_MATCHER.match(uri);
@ -956,47 +883,11 @@ public final class Utils implements Constants {
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) {
if (isMyRetweet(status)) return true;
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) {
if (context == null) return;
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)));
}
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) {
if (orig == null) return null;
return orig.replaceAll("\\n+", "\n");
@ -1228,21 +1106,10 @@ public final class Utils implements Constants {
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() {
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) {
if (!uri.getBooleanQueryParameter(QUERY_PARAM_FROM_NOTIFICATION, false)) return;
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);
}
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
*

View File

@ -24,47 +24,42 @@ import java.util.Collection;
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) {
addAll(collection);
}
public NoDuplicatesArrayList(final Collection<? extends E> collection) {
addAll(collection);
}
public NoDuplicatesArrayList(final int capacity) {
super(capacity);
}
public NoDuplicatesArrayList(final int capacity) {
super(capacity);
}
@Override
public boolean add(final E e) {
if (contains(e))
return false;
else
return super.add(e);
}
@Override
public boolean add(final E e) {
if (contains(e)) return false;
return super.add(e);
}
@Override
public void add(final int index, final E element) {
if (contains(element))
return;
else {
super.add(index, element);
}
}
@Override
public void add(final int index, final E element) {
if (contains(element)) return;
super.add(index, element);
}
@Override
public boolean addAll(final Collection<? extends E> collection) {
final Collection<E> copy = new ArrayList<>(collection);
copy.removeAll(this);
return super.addAll(copy);
}
@Override
public boolean addAll(final Collection<? extends E> collection) {
final Collection<E> copy = new ArrayList<>(collection);
copy.removeAll(this);
return super.addAll(copy);
}
@Override
public boolean addAll(final int index, final Collection<? extends E> collection) {
final Collection<E> copy = new ArrayList<>(collection);
copy.removeAll(this);
return super.addAll(index, copy);
}
@Override
public boolean addAll(final int index, final Collection<? extends E> collection) {
final Collection<E> copy = new ArrayList<>(collection);
copy.removeAll(this);
return super.addAll(index, copy);
}
}

View File

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

View File

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

View File

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

View File

@ -7,15 +7,11 @@ import android.text.TextUtils;
import org.mariotaku.restfu.http.RestHttpClient;
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.Provider;
import org.mariotaku.twidere.util.media.preview.provider.TwitterMediaProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by mariotaku on 16/1/1.
@ -61,17 +57,4 @@ public class PreviewMediaExtractor {
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;
}
}

View File

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

View File

@ -37,6 +37,7 @@ public class DefaultWebViewClient extends WebViewClient {
}
@Override
@Deprecated
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
try {
mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));

View File

@ -89,7 +89,6 @@ public class ForegroundColorView extends View implements IForegroundView {
* the padding area.
*
* @param drawable The Drawable to be drawn on top of the children.
* @attr ref android.R.styleable#FrameLayout_foreground
*/
@Override
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.
*
* @param foregroundGravity See {@link android.view.Gravity}
* @attr ref android.R.styleable#FrameLayout_foregroundGravity
*/
@Override
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;
mAlphaPattern = alphaPattern;
invalidate();

View File

@ -64,7 +64,6 @@ public class ForegroundImageView extends ImageView implements IForegroundView {
* the padding area.
*
* @param drawable The Drawable to be drawn on top of the children.
* @attr ref android.R.styleable#FrameLayout_foreground
*/
@Override
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.
*
* @param foregroundGravity See {@link android.view.Gravity}
* @attr ref android.R.styleable#FrameLayout_foregroundGravity
*/
@Override
public void setForegroundGravity(final int foregroundGravity) {

View File

@ -26,7 +26,6 @@ import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
@ -92,7 +91,7 @@ public class LinePageIndicator extends View implements PagerIndicator {
a.recycle();
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
mTouchSlop = configuration.getScaledPagingTouchSlop();
}
public float getGapWidth() {
@ -221,13 +220,13 @@ public class LinePageIndicator extends View implements PagerIndicator {
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mActivePointerId = ev.getPointerId(0);
mLastMotionX = ev.getX();
break;
case MotionEvent.ACTION_MOVE: {
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
final float deltaX = x - mLastMotionX;
if (!mIsDragging) {
@ -276,19 +275,19 @@ public class LinePageIndicator extends View implements PagerIndicator {
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
mLastMotionX = MotionEventCompat.getX(ev, index);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
mLastMotionX = ev.getX(index);
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
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;
}

View File

@ -64,6 +64,7 @@ public class ProfileBannerImageView extends ForegroundImageView implements IExte
}
@Override
@Deprecated
protected boolean fitSystemWindows(@NonNull Rect insets) {
if (mOnFitSystemWindowsListener != null) {
mOnFitSystemWindowsListener.onFitSystemWindows(insets);

View File

@ -39,8 +39,4 @@ public class ViewListHolder {
return view.getContext();
}
protected String getString(final int resId, final Object... formatArgs) {
return getContext().getString(resId, formatArgs);
}
}

View File

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

View File

@ -50,7 +50,6 @@ public interface IForegroundView {
* the padding area.
*
* @param drawable The Drawable to be drawn on top of the children.
* @attr ref android.R.attr#foreground
*/
void setForeground(final Drawable drawable);
@ -58,7 +57,6 @@ public interface IForegroundView {
* Describes how the foreground is positioned. Defaults to START and TOP.
*
* @param foregroundGravity See {@link android.view.Gravity}
* @attr ref android.R.attr#foregroundGravity
*/
void setForegroundGravity(int foregroundGravity);

View File

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

View File

@ -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
* views are drawn on screen (e.g., default start page).
* </p>
*
* @param item
*/
void setCurrentItem(int item);
/**
* Set a page change listener which will receive forwarded events.
*
* @param listener
*/
void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
/**
* Bind the indicator to a ViewPager.
*
* @param view
*/
void setViewPager(ViewPager view);
/**
* Bind the indicator to a ViewPager.
*
* @param view
* @param 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
*
* @param position
* @return
*/
Drawable getPageIcon(int position);
/**
* Returns the title of the view at position
*
* @param position
* @return
*/
CharSequence getPageTitle(int position);

View File

@ -67,6 +67,7 @@ import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.addOnAccountsUpdatedListenerSafe
import org.mariotaku.ktextension.coerceInOr
import org.mariotaku.ktextension.convert
import org.mariotaku.ktextension.removeOnAccountsUpdatedListenerSafe
import org.mariotaku.twidere.Constants.*
@ -524,7 +525,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
override fun onNewIntent(intent: Intent) {
val tabPosition = handleIntent(intent, false)
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) {
val rememberPosition = preferences.getBoolean(SharedPreferenceConstants.KEY_REMEMBER_POSITION, true)
if (initialTab >= 0) {
mainPager.currentItem = TwidereMathUtils.clamp(initialTab, pagerAdapter.count, 0)
mainPager.currentItem = initialTab.coerceInOr(0 until pagerAdapter.count, 0)
} else if (rememberPosition) {
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)
}
}

View File

@ -12,6 +12,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.bluelinelabs.logansquare.LoganSquare
import com.rengwuxian.materialedittext.MaterialEditText
import org.mariotaku.restfu.annotation.method.GET
import org.mariotaku.restfu.http.HttpRequest
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.util.ParseUtils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator
import java.io.IOException
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 editSameOAuthSigningUrl by lazy { dialog.findViewById(R.id.editSameOAuthSigningUrl) 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 editConsumerSecret by lazy { dialog.findViewById(R.id.editConsumerSecret) 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 MaterialEditText }
private val editAuthType by lazy { dialog.findViewById(R.id.editAuthType) as RadioGroup }
private val apiFormatHelpButton by lazy { dialog.findViewById(R.id.apiUrlFormatHelp) }
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 {
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 ->
val targetFragment = this.targetFragment
val parentFragment = this.parentFragment
@ -77,6 +79,10 @@ class APIEditorDialogFragment : BaseDialogFragment() {
}
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 }
editAuthType.setOnCheckedChangeListener { group, checkedId ->
val authType = getCheckedAuthType(checkedId)

View File

@ -128,9 +128,9 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks<AccountsInfo>,
hasPrevAccountIndicator.alpha = 0f
hasNextAccountIndicator.alpha = 0f
} else {
hasPrevAccountIndicator.alpha = TwidereMathUtils.clamp(pagePosition, 0f, 1f)
hasNextAccountIndicator.alpha = TwidereMathUtils.clamp(pageCount - (pagePosition
+ visiblePages), 0f, 1f)
hasPrevAccountIndicator.alpha = pagePosition.coerceIn(0f, 1f)
hasNextAccountIndicator.alpha = (pageCount - (pagePosition + visiblePages))
.coerceIn(0f, 1f)
}
}
})

View File

@ -1480,7 +1480,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
private var recyclerView: RecyclerView? = null
private var statusViewHolder: DetailStatusViewHolder? = null
private val itemCounts: IntArray
private val itemCounts = ItemCounts(ITEM_TYPES_SUM)
override val nameFirst: Boolean
private val cardBackgroundColor: Int
@ -1525,7 +1525,6 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
init {
setHasStableIds(true)
val context = fragment.activity
itemCounts = IntArray(ITEM_TYPES_SUM)
// There's always a space at the end of the list
itemCounts[ITEM_IDX_SPACE] = 1
itemCounts[ITEM_IDX_STATUS] = 1
@ -1570,7 +1569,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
fun getIndexStart(index: Int): Int {
if (index == 0) return 0
return TwidereMathUtils.sum(itemCounts, 0, index - 1)
return itemCounts.getItemStartPosition(index)
}
override fun getStatusId(position: Int): String? {
@ -1827,7 +1826,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
override fun getItemCount(): Int {
if (status == null) return 0
return TwidereMathUtils.sum(itemCounts)
return itemCounts.itemCount
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {

View File

@ -1398,7 +1398,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
private fun updateScrollOffset(offset: Int) {
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()
profileBanner.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 tabOutlineAlphaFactor: Float
if (offset - spaceHeight > 0) {
tabOutlineAlphaFactor = 1f - TwidereMathUtils.clamp((offset - spaceHeight) / profileContentHeight, 0f, 1f)
tabOutlineAlphaFactor = 1f - ((offset - spaceHeight) / profileContentHeight).coerceIn(0f, 1f)
} else {
tabOutlineAlphaFactor = 1f
}
@ -1467,7 +1467,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
val location = IntArray(2)
profileNameContainer.name.getLocationInWindow(location)
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)
if (titleView != null) {
titleView.alpha = textAlpha
@ -1545,10 +1545,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
}
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
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
invalidateSelf()
}

View File

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

View File

@ -35,7 +35,6 @@ import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
import org.mariotaku.twidere.util.InternalTwitterContentUtils
import org.mariotaku.twidere.util.Nullables
import java.util.*
class ConversationLoader(
@ -55,7 +54,7 @@ class ConversationLoader(
private var canLoadAllReplies: Boolean = false
init {
this.status = Nullables.assertNonNull(ParcelUtils.clone(status))
this.status = ParcelUtils.clone(status)
ParcelableStatusUtils.makeOriginalStatus(this.status)
}

View File

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

View File

@ -9,8 +9,8 @@ class ItemCounts(counts: Int) {
fun getItemCountIndex(itemPosition: Int): Int {
var sum = 0
for (i in data.indices) {
sum += data[i]
data.forEachIndexed { i, num ->
sum += num
if (itemPosition < sum) {
return i
}
@ -19,11 +19,7 @@ class ItemCounts(counts: Int) {
}
fun getItemStartPosition(countIndex: Int): Int {
var sum = 0
for (i in 0..countIndex - 1) {
sum += data[i]
}
return sum
return (0..countIndex - 1).sumBy { data[it] }
}
val itemCount: Int get() = data.sum()
@ -34,4 +30,8 @@ class ItemCounts(counts: Int) {
data[countIndex] = value
}
operator fun get(countIndex: Int): Int {
return data[countIndex]
}
}

View File

@ -137,6 +137,8 @@ class UpdateStatusTask(
} finally {
// Cleanup
pendingUpdate.deleteAlways.forEach { item -> item.delete(context) }
uploader?.unbindService()
shortener?.unbindService()
}
return result
}

View File

@ -82,8 +82,7 @@ object HttpClientFactory {
val proxyType = prefs.getString(KEY_PROXY_TYPE, null)
val proxyHost = prefs.getString(KEY_PROXY_HOST, null)
val proxyPort = NumberUtils.toInt(prefs.getString(KEY_PROXY_PORT, null), -1)
if (!isEmpty(proxyHost) && TwidereMathUtils.inRange(proxyPort, 0, 65535,
TwidereMathUtils.RANGE_INCLUSIVE_INCLUSIVE)) {
if (!isEmpty(proxyHost) && proxyPort in (0..65535)) {
val type = getProxyType(proxyType)
if (type != Proxy.Type.DIRECT) {
builder.proxy(Proxy(type, InetSocketAddress.createUnresolved(proxyHost, proxyPort)))

View File

@ -77,8 +77,8 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
.build()
fun displayPreviewImage(view: ImageView, uri: String?) {
imageLoader.displayImage(uri, view, previewDisplayOptions)
fun displayPreviewImage(view: ImageView, url: String?) {
imageLoader.displayImage(url, view, previewDisplayOptions)
}
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)
}
fun loadImageSync(uri: String, targetImageSize: ImageSize, options: DisplayImageOptions): Bitmap? {
return imageLoader.loadImageSync(uri, targetImageSize, options)
fun loadImageSync(url: String, targetImageSize: ImageSize, options: DisplayImageOptions): Bitmap? {
return imageLoader.loadImageSync(url, targetImageSize, options)
}
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)
}
fun displayProfileImage(view: ImageView, url: String, listener: ImageLoadingListener) {
fun displayProfileImage(view: ImageView, url: String?, listener: ImageLoadingListener) {
imageLoader.displayImage(url, view, profileImageDisplayOptions, listener)
}
@ -206,7 +206,7 @@ class MediaLoaderWrapper(val imageLoader: ImageLoader) {
preloadOnWifiOnly = preferences[mediaPreloadOnWifiOnlyKey]
}
private fun displayDashboardProfileImage(view: ImageView, url: String, drawableOnLoading: Drawable?) {
private fun displayDashboardProfileImage(view: ImageView, url: String?, drawableOnLoading: Drawable?) {
if (drawableOnLoading != null) {
val builder = Builder()
builder.cloneFrom(dashboardProfileImageDisplayOptions)

View File

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

View File

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

View File

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

View File

@ -47,6 +47,7 @@ import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
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.util.*
import org.mariotaku.twidere.util.imageloader.ReadOnlyDiskLRUNameCache
@ -310,7 +311,7 @@ class ApplicationModule(private val application: Application) {
val cacheDir = Utils.getExternalCacheDir(application, dirName)
val fallbackCacheDir = Utils.getInternalCacheDir(application, dirName)
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 {
val cacheMaxSizeBytes = cacheSize * 1024 * 1024
if (cacheDir != null)

View File

@ -17,25 +17,19 @@
* 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.
*/
public class ConsumerKeySecretValidator extends METValidator {
public ConsumerKeySecretValidator(String errorMessage) {
super(errorMessage);
}
class ConsumerKeySecretValidator(errorMessage: String) : METValidator(errorMessage) {
@Override
public boolean isValid(@NonNull CharSequence text, boolean isEmpty) {
return MicroBlogAPIFactory.isValidConsumerKeySecret(text);
override fun isValid(text: CharSequence, isEmpty: Boolean): Boolean {
return MicroBlogAPIFactory.isValidConsumerKeySecret(text)
}
}

View File

@ -9,6 +9,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import org.mariotaku.ktextension.coerceInOr
/**
* 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 {
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 {

View File

@ -29,7 +29,7 @@
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="0dp"
android:paddingStart="@dimen/element_spacing_normal"
tools:showIn="@layout/layout_api_editor">
tools:showIn="@layout/dialog_api_editor">
<CheckBox
android:id="@+id/editSameOAuthSigningUrl"