diff --git a/global.gradle b/global.gradle index af4fcfc4a..cbe7c4d80 100644 --- a/global.gradle +++ b/global.gradle @@ -22,6 +22,7 @@ android { lintOptions { abortOnError false + lintConfig rootProject.file('lint.xml') } packagingOptions { diff --git a/lint.xml b/lint.xml new file mode 100644 index 000000000..650f7796d --- /dev/null +++ b/lint.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/twidere/build.gradle b/twidere/build.gradle index 3bd39d9ed..7ed30ca36 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -14,7 +14,7 @@ android { applicationId "org.mariotaku.twidere" minSdkVersion 14 targetSdkVersion 23 - versionCode 131 + versionCode 133 versionName "0.3.0" multiDexEnabled true } diff --git a/twidere/src/main/java/edu/tsinghua/hotmobi/HotMobiLogger.java b/twidere/src/main/java/edu/tsinghua/hotmobi/HotMobiLogger.java index 03523d611..5ecc41247 100644 --- a/twidere/src/main/java/edu/tsinghua/hotmobi/HotMobiLogger.java +++ b/twidere/src/main/java/edu/tsinghua/hotmobi/HotMobiLogger.java @@ -27,7 +27,9 @@ import android.content.SharedPreferences; import android.location.Location; import android.os.BatteryManager; import android.text.TextUtils; +import android.util.Log; +import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.util.JsonSerializer; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 13c6bb89b..7972b5e12 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -56,7 +56,6 @@ import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; -import android.text.method.ArrowKeyMovementMethod; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.URLSpan; @@ -859,7 +858,10 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac quotedNameView.setText(manager.getUserNickname(status.quoted_user_id, status.quoted_user_name, false)); quotedScreenNameView.setText("@" + status.quoted_user_screen_name); - quotedTextView.setText(HtmlSpanBuilder.fromHtml(status.quoted_text_html)); + final Spanned quotedText = HtmlSpanBuilder.fromHtml(status.quoted_text_html); + if (!TextUtils.equals(quotedTextView.getText(), quotedText)) { + quotedTextView.setText(quotedText); + } linkify.applyAllLinks(quotedTextView, status.account_id, layoutPosition, status.is_possibly_sensitive); ThemeUtils.applyParagraphSpacing(quotedTextView, 1.1f); @@ -910,7 +912,9 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac } timeSourceView.setMovementMethod(LinkMovementMethod.getInstance()); - textView.setText(HtmlSpanBuilder.fromHtml(status.text_html)); + final Spanned text = HtmlSpanBuilder.fromHtml(status.text_html); + if (!TextUtils.equals(textView.getText(), text)) + textView.setText(text); linkify.applyAllLinks(textView, status.account_id, layoutPosition, status.is_possibly_sensitive); ThemeUtils.applyParagraphSpacing(textView, 1.1f); @@ -982,8 +986,8 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac textView.setTextIsSelectable(true); quotedTextView.setTextIsSelectable(true); - textView.setMovementMethod(ArrowKeyMovementMethod.getInstance()); - quotedTextView.setMovementMethod(ArrowKeyMovementMethod.getInstance()); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + quotedTextView.setMovementMethod(LinkMovementMethod.getInstance()); } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java index 8832c8e24..a50b7f74c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java @@ -509,12 +509,10 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener mDescriptionView.setText(user.description_html != null ? HtmlSpanBuilder.fromHtml(user.description_html) : user.description_plain); final TwidereLinkify linkify = new TwidereLinkify(this); linkify.applyAllLinks(mDescriptionView, user.account_id, false); - mDescriptionView.setMovementMethod(null); mLocationContainer.setVisibility(TextUtils.isEmpty(user.location) ? View.GONE : View.VISIBLE); mLocationView.setText(user.location); mURLContainer.setVisibility(TextUtils.isEmpty(user.url) && TextUtils.isEmpty(user.url_expanded) ? View.GONE : View.VISIBLE); mURLView.setText(TextUtils.isEmpty(user.url_expanded) ? user.url : user.url_expanded); - mURLView.setMovementMethod(null); final String createdAt = Utils.formatToLongTimeString(activity, user.created_at); final float daysSinceCreation = (System.currentTimeMillis() - user.created_at) / 1000 / 60 / 60 / 24; final int dailyTweets = Math.round(user.statuses_count / Math.max(1, daysSinceCreation)); diff --git a/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java b/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java index 32160dda2..42835a9bc 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java +++ b/twidere/src/main/java/org/mariotaku/twidere/service/RefreshService.java @@ -156,7 +156,7 @@ public class RefreshService extends Service implements Constants { break; } case Intent.ACTION_SCREEN_OFF: { - HotMobiLogger.logScreenEvent(context, ScreenEvent.Action.ON); + HotMobiLogger.logScreenEvent(context, ScreenEvent.Action.OFF); break; } } @@ -173,7 +173,6 @@ public class RefreshService extends Service implements Constants { super.onCreate(); DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(this)).build().inject(this); mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); - final TwidereApplication app = TwidereApplication.getInstance(this); mPendingRefreshHomeTimelineIntent = PendingIntent.getBroadcast(this, 0, new Intent( BROADCAST_REFRESH_HOME_TIMELINE), 0); mPendingRefreshMentionsIntent = PendingIntent.getBroadcast(this, 0, new Intent(BROADCAST_REFRESH_MENTIONS), 0); diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereNetwork.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereNetwork.java index 46637bf2d..02583222a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereNetwork.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereNetwork.java @@ -30,6 +30,7 @@ import com.squareup.okhttp.internal.Network; import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.util.HostsFileParser; +import org.mariotaku.twidere.util.SharedPreferencesWrapper; import org.xbill.DNS.AAAARecord; import org.xbill.DNS.ARecord; import org.xbill.DNS.CNAMERecord; @@ -65,7 +66,7 @@ public class TwidereNetwork implements Constants, Network { private Resolver mDns; public TwidereNetwork(final Context context) { - mHostMapping = context.getSharedPreferences(HOST_MAPPING_PREFERENCES_NAME, Context.MODE_PRIVATE); + mHostMapping = SharedPreferencesWrapper.getInstance(context, HOST_MAPPING_PREFERENCES_NAME, Context.MODE_PRIVATE); mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); final String address = mPreferences.getString(KEY_DNS_SERVER, DEFAULT_DNS_SERVER_ADDRESS); mDnsAddress = isValidIpAddress(address) ? address : DEFAULT_DNS_SERVER_ADDRESS; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/HandleSpanClickTextView.java b/twidere/src/main/java/org/mariotaku/twidere/view/HandleSpanClickTextView.java deleted file mode 100644 index 5aa9c15cb..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/view/HandleSpanClickTextView.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.mariotaku.twidere.view; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.text.Layout; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.style.ClickableSpan; -import android.util.AttributeSet; -import android.view.MotionEvent; - -import org.mariotaku.twidere.view.themed.ThemedTextView; - -public class HandleSpanClickTextView extends ThemedTextView { - - private boolean mLongClickPerformed; - - public HandleSpanClickTextView(final Context context) { - super(context); - } - - public HandleSpanClickTextView(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - public HandleSpanClickTextView(final Context context, final AttributeSet attrs, final int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean onTouchEvent(@NonNull final MotionEvent event) { - final Spannable buffer = SpannableString.valueOf(getText()); - final int action = event.getAction(); - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= getTotalPaddingLeft(); - y -= getTotalPaddingTop(); - - x += getScrollX(); - y += getScrollY(); - - final Layout layout = getLayout(); - final int line = layout.getLineForVertical(y); - final int off; - try { - off = layout.getOffsetForHorizontal(line, x); - } catch (IndexOutOfBoundsException e) { - throw new IndexOutOfBoundsException("Line count " + layout.getLineCount() + - ", line for y " + y + " is " + line + ", x is " + x); - } - final float lineWidth = layout.getLineWidth(line); - - final ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class); - - if (links.length != 0 && x <= lineWidth) { - final ClickableSpan link = links[0]; - if (action == MotionEvent.ACTION_UP) { - setClickable(false); - if (!mLongClickPerformed) { - link.onClick(this); - } - return true; - } else { - mLongClickPerformed = false; - setClickable(true); - } - } else { - setClickable(false); - } - } - return super.onTouchEvent(event); - } - - - @Override - public boolean performLongClick() { - final boolean result = super.performLongClick(); - mLongClickPerformed = true; - return result; - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/StatusTextView.java b/twidere/src/main/java/org/mariotaku/twidere/view/StatusTextView.java index 0ffc99ccd..f767725e4 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/StatusTextView.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/StatusTextView.java @@ -1,26 +1,45 @@ package org.mariotaku.twidere.view; import android.content.Context; -import android.support.v7.widget.AppCompatTextView; import android.text.Editable; import android.text.Spannable; import android.util.AttributeSet; +import android.view.MotionEvent; import org.mariotaku.twidere.text.SafeSpannableString; import org.mariotaku.twidere.text.SafeSpannableStringBuilder; +import org.mariotaku.twidere.view.themed.ThemedTextView; -public class StatusTextView extends HandleSpanClickTextView { +public class StatusTextView extends ThemedTextView { public StatusTextView(final Context context) { - this(context, null); + super(context); + init(); } public StatusTextView(final Context context, final AttributeSet attrs) { - this(context, attrs, 0); + super(context, attrs); + init(); } public StatusTextView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); + init(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + // FIXME simple workaround to https://code.google.com/p/android/issues/detail?id=191430 + // Android clears TextView when setText(), so setText before touch + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + final CharSequence text = getText(); + setText(null); + setText(text); + } + return super.dispatchTouchEvent(event); + } + + private void init() { setEditableFactory(new SafeEditableFactory()); setSpannableFactory(new SafeSpannableFactory()); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/TimelineContentTextView.java b/twidere/src/main/java/org/mariotaku/twidere/view/TimelineContentTextView.java new file mode 100644 index 000000000..2bf2a1ed8 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/view/TimelineContentTextView.java @@ -0,0 +1,87 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.view; + +import android.content.Context; +import android.text.Layout; +import android.text.Spannable; +import android.text.method.LinkMovementMethod; +import android.text.method.MovementMethod; +import android.text.style.ClickableSpan; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import org.mariotaku.twidere.view.themed.ThemedTextView; + +/** + * Returns true when not clicking links + * Created by mariotaku on 15/11/20. + */ +public class TimelineContentTextView extends ThemedTextView { + private boolean mFirstNotLink; + + public TimelineContentTextView(Context context) { + super(context); + } + + public TimelineContentTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public TimelineContentTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected MovementMethod getDefaultMovementMethod() { + return LinkMovementMethod.getInstance(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + Layout layout = getLayout(); + final float x = event.getX() - getPaddingLeft() + getScrollX(); + final float y = event.getY() - getPaddingTop() + getScrollY(); + final int line = layout.getLineForVertical(Math.round(y)); + int offset = layout.getOffsetForHorizontal(line, x); + final CharSequence text = getText(); + if (text instanceof Spannable) { + final ClickableSpan[] spans = ((Spannable) text).getSpans(offset, offset, ClickableSpan.class); + mFirstNotLink = spans.length == 0; + } else { + mFirstNotLink = true; + } + break; + } + case MotionEvent.ACTION_UP: { + mFirstNotLink = false; + break; + } + } + if (mFirstNotLink) { + super.onTouchEvent(event); + return false; + } else { + return super.onTouchEvent(event); + } + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java index 4e08086a4..2f1a32cb3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java @@ -124,7 +124,6 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi } else { textView.setText(toPlainText(TWIDERE_PREVIEW_TEXT_HTML)); } - textView.setMovementMethod(null); timeView.setTime(System.currentTimeMillis()); mediaPreview.setVisibility(adapter.isMediaPreviewEnabled() ? View.VISIBLE : View.GONE); mediaPreview.displayMedia(R.drawable.nyan_stars_background); @@ -185,7 +184,6 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi quotedTextView.setText(text); linkify.applyAllLinks(quotedTextView, status.account_id, getLayoutPosition(), status.is_possibly_sensitive, adapter.getLinkHighlightingStyle()); - quotedTextView.setMovementMethod(null); } else { final String text = status.quoted_text_unescaped; quotedTextView.setText(text); @@ -265,7 +263,6 @@ public class StatusViewHolder extends ViewHolder implements Constants, OnClickLi linkify.applyAllLinks(textView, status.account_id, getLayoutPosition(), status.is_possibly_sensitive, adapter.getLinkHighlightingStyle()); - textView.setMovementMethod(null); } final Locale locale = Locale.getDefault(); diff --git a/twidere/src/main/res/layout/card_item_message_conversation_common.xml b/twidere/src/main/res/layout/card_item_message_conversation_common.xml index 59fd1f57d..1230140c7 100644 --- a/twidere/src/main/res/layout/card_item_message_conversation_common.xml +++ b/twidere/src/main/res/layout/card_item_message_conversation_common.xml @@ -39,7 +39,7 @@ android:orientation="vertical" android:padding="@dimen/element_spacing_normal"> - - - - - - - - - - - - - Twitter optimized searches Use special search terms to improve search results like exclude retweets I want my stars back! - Show use favorite (star) instead of like (heart) + Use favorite (★) instead of like (♥︎) Copy link Link copied to clipboard Login verification