From d1c26006c477d19a59f484b8c8e89001910af2a8 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Tue, 10 Nov 2015 16:03:03 +0800 Subject: [PATCH] improved auto complete --- build.gradle | 2 - gradle/wrapper/gradle-wrapper.jar | Bin 52271 -> 53636 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- twidere/build.gradle | 3 +- twidere/src/main/AndroidManifest.xml | 4 + .../java/jopt/csp/util/SortableIntList.java | 291 +++++++++++++ .../twidere/activity/CopyLinkActivity.java | 39 ++ .../support/QuickSearchBarActivity.java | 116 +++-- .../twidere/adapter/AbsActivitiesAdapter.java | 10 +- .../adapter/iface/IActivitiesAdapter.java | 7 +- .../fragment/support/AbsStatusesFragment.java | 5 +- .../support/RetweetQuoteDialogFragment.java | 2 +- .../fragment/support/StatusFragment.java | 2 +- .../fragment/support/UserFragment.java | 6 +- .../fragment/support/UserListFragment.java | 2 +- .../support/UserListMembersFragment.java | 27 +- .../twidere/provider/TwidereDataProvider.java | 12 +- .../twidere/util/AsyncTwitterWrapper.java | 38 +- .../mariotaku/twidere/util/LinkCreator.java | 5 + .../SwipeDismissListViewTouchListener.java | 403 ++++++++++++++++++ .../org/mariotaku/twidere/util/Utils.java | 24 +- .../ActivityTitleSummaryViewHolder.java | 58 ++- .../ic_activity_action_favorite.png | Bin 0 -> 696 bytes .../drawable-hdpi/ic_activity_action_like.png | Bin 0 -> 621 bytes .../ic_activity_action_favorite.png | Bin 0 -> 505 bytes .../drawable-mdpi/ic_activity_action_like.png | Bin 0 -> 482 bytes .../ic_activity_action_favorite.png | Bin 0 -> 860 bytes .../ic_activity_action_like.png | Bin 0 -> 780 bytes .../ic_activity_action_favorite.png | Bin 0 -> 1258 bytes .../ic_activity_action_like.png | Bin 0 -> 1134 bytes .../ic_activity_action_favorite.png | Bin 0 -> 1616 bytes .../ic_activity_action_like.png | Bin 0 -> 1436 bytes .../ic_activity_action_favorite.png | Bin 360 -> 0 bytes .../ic_activity_action_favorite.png | Bin 254 -> 0 bytes .../ic_activity_action_favorite.png | Bin 458 -> 0 bytes .../ic_activity_action_favorite.png | Bin 673 -> 0 bytes .../res/layout/list_item_compose_account.xml | 43 ++ .../src/main/res/menu/menu_user_profile.xml | 1 + twidere/src/main/res/values/strings.xml | 8 + .../ic_activity_action_favorite-mdpi.svg | 5 + .../drawable/ic_activity_action_like-mdpi.svg | 12 + 41 files changed, 1024 insertions(+), 105 deletions(-) create mode 100644 twidere/src/main/java/jopt/csp/util/SortableIntList.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/activity/CopyLinkActivity.java create mode 100644 twidere/src/main/java/org/mariotaku/twidere/util/SwipeDismissListViewTouchListener.java create mode 100644 twidere/src/main/res-svg2png/drawable-hdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res-svg2png/drawable-hdpi/ic_activity_action_like.png create mode 100644 twidere/src/main/res-svg2png/drawable-mdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res-svg2png/drawable-mdpi/ic_activity_action_like.png create mode 100644 twidere/src/main/res-svg2png/drawable-xhdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res-svg2png/drawable-xhdpi/ic_activity_action_like.png create mode 100644 twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_like.png create mode 100644 twidere/src/main/res-svg2png/drawable-xxxhdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res-svg2png/drawable-xxxhdpi/ic_activity_action_like.png delete mode 100755 twidere/src/main/res/drawable-hdpi/ic_activity_action_favorite.png delete mode 100755 twidere/src/main/res/drawable-mdpi/ic_activity_action_favorite.png delete mode 100755 twidere/src/main/res/drawable-xhdpi/ic_activity_action_favorite.png delete mode 100755 twidere/src/main/res/drawable-xxhdpi/ic_activity_action_favorite.png create mode 100644 twidere/src/main/res/layout/list_item_compose_account.xml create mode 100644 twidere/src/main/svg/drawable/ic_activity_action_favorite-mdpi.svg create mode 100644 twidere/src/main/svg/drawable/ic_activity_action_like-mdpi.svg diff --git a/build.gradle b/build.gradle index 301f12951..888109126 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.github.ben-manes.versions' -apply plugin: 'com.github.hierynomus.license' // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { @@ -15,7 +14,6 @@ buildscript { // should be excluded to avoid conflict exclude group: 'xerces' } - classpath 'gradle.plugin.nl.javadude.gradle.plugins:license-gradle-plugin:0.12.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 30d399d8d2bf522ff5de94bf434a7cc43a9a74b5..fd7e590e5154e82211909581e71018372134fb27 100644 GIT binary patch delta 12481 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 delta 11068 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 L=0Zei{iFSVWrPWg diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 50c99ee33..5d18332f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Aug 19 14:17:02 CST 2015 +#Sun Nov 08 17:18:47 CST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip diff --git a/twidere/build.gradle b/twidere/build.gradle index 5a6574f82..e8e5eb6a8 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -93,11 +93,12 @@ dependencies { compile 'com.soundcloud.android:android-crop:1.0.1@aar' compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1' compile 'com.github.mariotaku:PickNCrop:1dff3ed574' + compile 'com.github.mariotaku:RestFu:0.9.2' compile 'com.diogobernardino:williamchart:2.0.1' compile 'com.lnikkila:extendedtouchview:0.1.0' compile 'com.google.dagger:dagger:2.0.1' compile 'org.attoparser:attoparser:1.4.0.RELEASE' - googleCompile 'com.google.android.gms:play-services-maps:8.1.0' + googleCompile 'com.google.android.gms:play-services-maps:8.3.0' googleCompile 'com.google.maps.android:android-maps-utils:0.4' fdroidCompile 'org.osmdroid:osmdroid-android:4.3' fdroidCompile 'org.slf4j:slf4j-simple:1.7.12' diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml index 2b75df2a3..135c5eba2 100644 --- a/twidere/src/main/AndroidManifest.xml +++ b/twidere/src/main/AndroidManifest.xml @@ -341,6 +341,10 @@ + + * + * 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 . + */ + +/* + * Created on Nov 21, 2005 + */ +package jopt.csp.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Arrays; + +import org.apache.commons.collections.primitives.ArrayIntList; +import org.apache.commons.collections.primitives.IntCollection; +import org.apache.commons.collections.primitives.IntList; +import org.apache.commons.collections.primitives.RandomAccessIntList; + + +/** + * A flexible, sortable list of int primitives. Borrows much + * of its functionality from the ArrayIntList implementation + * given in the Commons Primitives project + * (http://jakarta.apache.org/commons/primitives/index.html) + * + * @author Chris Johnson + */ +public class SortableIntList extends RandomAccessIntList implements IntList, Serializable { + + private transient int[] data = null; + private int size = 0; + + /** + * Construct an empty list with the default + * initial capacity. + */ + public SortableIntList() { + this(8); + } + + /** + * Construct an empty list with the given + * initial capacity. + * + * @throws IllegalArgumentException when initialCapacity is negative + */ + public SortableIntList(int initialCapacity) { + if (initialCapacity < 0) { + throw new IllegalArgumentException("capacity " + initialCapacity + " cannot be negative"); + } + data = new int[initialCapacity]; + size = 0; + } + + /** + * Constructs a list containing the elements of the given collection, + * in the order they are returned by that collection's iterator. + * + * @param that the non-null collection of ints + * to add + * @throws NullPointerException if that is null + * @see ArrayIntList#addAll(org.apache.commons.collections.primitives.IntCollection) + */ + public SortableIntList(IntCollection that) { + this(that.size()); + addAll(that); + } + + @Override + public int get(int index) { + checkRange(index); + return data[index]; + } + + @Override + public int size() { + return size; + } + + /** + * Removes the element at the specified position in + * (optional operation). Any subsequent elements + * are shifted to the left, subtracting one from their + * indices. Returns the element that was removed. + * + * @param index the index of the element to remove + * @return the value of the element that was removed + * @throws UnsupportedOperationException when this operation is not + * supported + * @throws IndexOutOfBoundsException if the specified index is out of range + */ + @Override + public int removeElementAt(int index) { + checkRange(index); + incrModCount(); + int oldval = data[index]; + int numtomove = size - index - 1; + if (numtomove > 0) { + System.arraycopy(data, index + 1, data, index, numtomove); + } + size--; + return oldval; + } + + /** + * Replaces the element at the specified + * position in me with the specified element + * (optional operation). If specified index is + * beyond the current size, the list grows + * to accommodate it. No IndexOutOfBoundsException + * will occur during the set operation. + * + * @param index the index of the element to change + * @param element the value to be stored at the specified position + * @return the value previously stored at the specified position + * @throws UnsupportedOperationException when this operation is not + * supported + */ + @Override + public int set(int index, int element) { + ensureCapacity(index + 1); + ensureSize(index + 1); + incrModCount(); + int oldval = data[index]; + data[index] = element; + return oldval; + } + + /** + * Inserts the specified element at the specified position + * (optional operation). Shifts the element currently + * at that position (if any) and any subsequent elements to the + * right, increasing their indices. If the specified index is + * beyond the current size, this method behaves like a call + * to {@link #set(int, int)}. + * + * @param index the index at which to insert the element + * @param element the value to insert + * @throws UnsupportedOperationException when this operation is not + * supported + * @throws IllegalArgumentException if some aspect of the specified element + * prevents it from being added to me + */ + @Override + public void add(int index, int element) { + if (index >= size) { + set(index, element); + } else { + incrModCount(); + ensureCapacity(size + 1); + int numtomove = size - index; + System.arraycopy(data, index, data, index + 1, numtomove); + data[index] = element; + size++; + } + } + + /** + * Increases my capacity, if necessary, to ensure that I can hold at + * least the number of elements specified by the minimum capacity + * argument without growing. + */ + public void ensureCapacity(int mincap) { + incrModCount(); + if (mincap > data.length) { + int newcap = (data.length * 3) / 2 + 1; + int[] olddata = data; + data = new int[newcap < mincap ? mincap : newcap]; + System.arraycopy(olddata, 0, data, 0, size); + } + } + + /** + * Reduce my capacity, if necessary, to match my + * current {@link #size size}. + */ + public void trimToSize() { + incrModCount(); + if (size < data.length) { + int[] olddata = data; + data = new int[size]; + System.arraycopy(olddata, 0, data, 0, size); + } + } + + /** + * Sorts the list into ascending numerical order via {@link java.util.Arrays#sort(int[])} + *

+ * Sorts the list of ints into ascending numerical order. The sorting algorithm + * is a tuned quicksort, adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering + * a Sort Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265 (November + * 1993). This algorithm offers n*log(n) performance on many data sets that cause other + * quicksorts to degrade to quadratic performance. + */ + public void sort() { + trimToSize(); + Arrays.sort(data); + } + + /** + * Reverses the order of the elements + */ + public void reverse() { + for (int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j--) + swap(i, j); + } + + /** + * Swaps the two specified elements. + * (If the specified positions are equal, invoking this method leaves + * the list unchanged.) + */ + public void swap(int i, int j) { + int tmp = data[i]; + data[i] = data[j]; + data[j] = tmp; + } + + /** + * Searches the list for the specified key via {@link java.util.Arrays#binarySearch(int[], int)} + *

+ * The array must be sorted (as by the sort method, above) prior to making this call. + * If it is not sorted, the results are undefined. If the list contains multiple elements + * with the specified value, there is no guarantee which one will be found. + * + * @param key the value to be searched for + * @return index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1) + */ + public int binarySearch(int key) { + trimToSize(); + return Arrays.binarySearch(data, key); + } + + /** + * Sorts the specified range of the list into ascending numerical order + * via {@link java.util.Arrays#sort(int[], int, int)} + * + * @param fromIndex the index of the first element (inclusive) to be sorted + * @param toIndex the index of the last element (exclusive) to be sorted + */ + public void sort(int fromIndex, int toIndex) { + trimToSize(); + Arrays.sort(data, fromIndex, toIndex); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeInt(data.length); + for (int i = 0; i < size; i++) { + out.writeInt(data[i]); + } + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + data = new int[in.readInt()]; + for (int i = 0; i < size; i++) { + data[i] = in.readInt(); + } + } + + private void ensureSize(int potentialSize) { + if (potentialSize > size) { + size = potentialSize; + } + } + + private void checkRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException("Should be at least 0 and less than " + size + ", found " + index); + } + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/CopyLinkActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/CopyLinkActivity.java new file mode 100644 index 000000000..8575c2ff9 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/CopyLinkActivity.java @@ -0,0 +1,39 @@ +/* + * 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.activity; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.Toast; + +import org.mariotaku.twidere.Constants; +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.util.ClipboardUtils; + +public class CopyLinkActivity extends Activity implements Constants { + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ClipboardUtils.setText(this, getIntent().getDataString()); + Toast.makeText(this, R.string.link_copied_to_clipboard, Toast.LENGTH_SHORT).show(); + finish(); + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/QuickSearchBarActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/QuickSearchBarActivity.java index b15418e10..484e4b765 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/QuickSearchBarActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/QuickSearchBarActivity.java @@ -19,6 +19,7 @@ package org.mariotaku.twidere.activity.support; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -44,38 +45,42 @@ import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; -import com.mobeta.android.dslv.DragSortCursorAdapter; - import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter; import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableCredentials; +import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory; import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions; import org.mariotaku.twidere.util.EditTextEnterHandler; import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener; import org.mariotaku.twidere.util.KeyboardShortcutsHandler; import org.mariotaku.twidere.util.MediaLoaderWrapper; import org.mariotaku.twidere.util.ParseUtils; +import org.mariotaku.twidere.util.SwipeDismissListViewTouchListener; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.UserColorNameManager; import org.mariotaku.twidere.util.Utils; +import org.mariotaku.twidere.util.content.ContentResolverUtils; import org.mariotaku.twidere.view.ExtendedRelativeLayout; import org.mariotaku.twidere.view.iface.IExtendedView.OnFitSystemWindowsListener; import java.util.List; +import jopt.csp.util.SortableIntList; + /** * Created by mariotaku on 15/1/6. */ public class QuickSearchBarActivity extends ThemedFragmentActivity implements OnClickListener, LoaderCallbacks, OnItemSelectedListener, OnItemClickListener, - OnFitSystemWindowsListener { + OnFitSystemWindowsListener, SwipeDismissListViewTouchListener.DismissCallbacks { private Spinner mAccountSpinner; private EditText mSearchQuery; @@ -86,19 +91,24 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On private Rect mSystemWindowsInsets = new Rect(); private boolean mTextChanged; + @Override + public boolean canDismiss(int position) { + return mUsersSearchAdapter.getItemViewType(position) == SuggestionsAdapter.VIEW_TYPE_SEARCH_HISTORY; + } + + @Override public void onDismiss(ListView listView, int[] reverseSortedPositions) { -// final long[] ids = new long[reverseSortedPositions.length]; -// for (int i = 0, j = reverseSortedPositions.length; i < j; i++) { -// final int position = reverseSortedPositions[i]; -// final SearchHistoryItem item = (SearchHistoryItem) mUsersSearchAdapter.getItem(position); -// mUsersSearchAdapter.removeItemAt(position); -// ids[i] = item.getCursorId(); -// } -// final ContentResolver cr = getContentResolver(); -// final Long[] idsObject = ArrayUtils.toObject(ids); -// ContentResolverUtils.bulkDelete(cr, SearchHistory.CONTENT_URI, SearchHistory._ID, idsObject, -// null, false); -// getSupportLoaderManager().restartLoader(0, null, this); + final Long[] ids = new Long[reverseSortedPositions.length]; + for (int i = 0, j = reverseSortedPositions.length; i < j; i++) { + final int position = reverseSortedPositions[i]; + final SuggestionItem item = mUsersSearchAdapter.getSuggestionItem(position); + ids[i] = item._id; + } + mUsersSearchAdapter.addRemovedPositions(reverseSortedPositions); + final ContentResolver cr = getContentResolver(); + ContentResolverUtils.bulkDelete(cr, SearchHistory.CONTENT_URI, SearchHistory._ID, ids, + null, false); + getSupportLoaderManager().restartLoader(0, null, this); } @Override @@ -224,6 +234,10 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On mUsersSearchAdapter = new SuggestionsAdapter(this); mSuggestionsList.setAdapter(mUsersSearchAdapter); mSuggestionsList.setOnItemClickListener(this); + + final SwipeDismissListViewTouchListener listener = new SwipeDismissListViewTouchListener(mSuggestionsList, this); + mSuggestionsList.setOnTouchListener(listener); + mSuggestionsList.setOnScrollListener(listener.makeScrollListener()); mSearchSubmit.setOnClickListener(this); EditTextEnterHandler.attach(mSearchQuery, new EnterListener() { @@ -296,16 +310,17 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On public final String title, summary; - private final long extra_id; + public final long _id, extra_id; public SuggestionItem(Cursor cursor, SuggestionsAdapter.Indices indices) { + _id = cursor.getLong(indices._id); title = cursor.getString(indices.title); summary = cursor.getString(indices.summary); extra_id = cursor.getLong(indices.extra_id); } } - public static class SuggestionsAdapter extends DragSortCursorAdapter implements OnClickListener { + public static class SuggestionsAdapter extends CursorAdapter implements OnClickListener { static final int VIEW_TYPE_SEARCH_HISTORY = 0; static final int VIEW_TYPE_SAVED_SEARCH = 1; @@ -316,24 +331,21 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On private final MediaLoaderWrapper mImageLoader; private final UserColorNameManager mUserColorNameManager; private final QuickSearchBarActivity mActivity; + private final SortableIntList mRemovedPositions; private Indices mIndices; SuggestionsAdapter(QuickSearchBarActivity activity) { super(activity, null, 0); + mRemovedPositions = new SortableIntList(); mActivity = activity; mImageLoader = activity.mImageLoader; mUserColorNameManager = activity.mUserColorNameManager; mInflater = LayoutInflater.from(activity); } - @Override - public long getItemId(int position) { - return position; - } - @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - switch (getItemViewType(cursor.getPosition())) { + switch (getActualItemViewType(cursor.getPosition())) { case VIEW_TYPE_SEARCH_HISTORY: case VIEW_TYPE_SAVED_SEARCH: { final View view = mInflater.inflate(R.layout.list_item_suggestion_search, parent, false); @@ -353,13 +365,13 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On } public SuggestionItem getSuggestionItem(int position) { - final Cursor cursor = (Cursor) super.getItem(position); + final Cursor cursor = (Cursor) getItem(position); return new SuggestionItem(cursor, mIndices); } @Override public void bindView(View view, Context context, Cursor cursor) { - switch (getItemViewType(cursor.getPosition())) { + switch (getActualItemViewType(cursor.getPosition())) { case VIEW_TYPE_SEARCH_HISTORY: { final SearchViewHolder holder = (SearchViewHolder) view.getTag(); final String title = cursor.getString(mIndices.title); @@ -400,7 +412,11 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On @Override public int getItemViewType(int position) { - final Cursor cursor = (Cursor) getItem(position); + return getActualItemViewType(getActualPosition(position)); + } + + public int getActualItemViewType(int position) { + final Cursor cursor = (Cursor) super.getItem(position); switch (cursor.getString(mIndices.type)) { case Suggestions.Search.TYPE_SAVED_SEARCH: { return VIEW_TYPE_SAVED_SEARCH; @@ -440,9 +456,55 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On } else { mIndices = null; } + mRemovedPositions.clear(); return super.swapCursor(newCursor); } + @Override + public int getCount() { + if (mRemovedPositions == null) return super.getCount(); + return super.getCount() - mRemovedPositions.size(); + } + + @Override + public Object getItem(int position) { + return super.getItem(getActualPosition(position)); + } + + @Override + public long getItemId(int position) { + return super.getItemId(getActualPosition(position)); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return super.getView(getActualPosition(position), convertView, parent); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return super.getDropDownView(getActualPosition(position), convertView, parent); + } + + private int getActualPosition(int position) { + if (mRemovedPositions == null) return position; + int skipped = 0; + for (int i = 0, j = mRemovedPositions.size(); i < j; i++) { + if (position + skipped >= mRemovedPositions.get(i)) { + skipped++; + } + } + return position + skipped; + } + + public void addRemovedPositions(int[] positions) { + for (int position : positions) { + mRemovedPositions.add(getActualPosition(position)); + } + mRemovedPositions.sort(); + notifyDataSetChanged(); + } + static class SearchViewHolder { private final ImageView icon; @@ -471,6 +533,7 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On } private static class Indices { + private final int _id; private final int type; private final int title; private final int summary; @@ -478,6 +541,7 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On private final int extra_id; public Indices(Cursor cursor) { + _id = cursor.getColumnIndex(Suggestions._ID); type = cursor.getColumnIndex(Suggestions.TYPE); title = cursor.getColumnIndex(Suggestions.TITLE); summary = cursor.getColumnIndex(Suggestions.SUMMARY); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java index caf8c0948..80d77dafa 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java @@ -58,7 +58,8 @@ import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder; * Created by mariotaku on 15/1/3. */ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter implements Constants, - IActivitiesAdapter, IStatusViewHolder.StatusClickListener, OnLinkClickListener, ActivityTitleSummaryViewHolder.ActivityClickListener { + IActivitiesAdapter, IStatusViewHolder.StatusClickListener, OnLinkClickListener, + ActivityTitleSummaryViewHolder.ActivityClickListener { private static final int ITEM_VIEW_TYPE_STUB = 0; private static final int ITEM_VIEW_TYPE_GAP = 1; @@ -75,6 +76,7 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< private final boolean mDisplayMediaPreview; private final boolean mNameFirst; private final boolean mDisplayProfileImage; + private final boolean mShouldUseStarsForLikes; private final TwidereLinkify mLinkify; private final DummyStatusHolderAdapter mStatusAdapterDelegate; private ActivityAdapterListener mActivityAdapterListener; @@ -87,6 +89,7 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< mInflater = LayoutInflater.from(context); mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress); mTextSize = mPreferences.getInt(KEY_TEXT_SIZE, context.getResources().getInteger(R.integer.default_text_size)); + mShouldUseStarsForLikes = mPreferences.getBoolean(KEY_I_WANT_MY_STARS_BACK); mCompactCards = compact; mProfileImageStyle = Utils.getProfileImageStyle(mPreferences.getString(KEY_PROFILE_IMAGE_STYLE, null)); mMediaPreviewStyle = Utils.getMediaPreviewStyle(mPreferences.getString(KEY_MEDIA_PREVIEW_STYLE, null)); @@ -175,6 +178,11 @@ public abstract class AbsActivitiesAdapter extends LoadMoreSupportAdapter< } + @Override + public boolean shouldUseStarsForLikes() { + return mShouldUseStarsForLikes; + } + @Override public void onUserProfileClick(IStatusViewHolder holder, int position) { final Context context = getContext(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java index 690785bfc..868781fea 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java @@ -19,10 +19,7 @@ package org.mariotaku.twidere.adapter.iface; -import android.support.annotation.NonNull; - import org.mariotaku.twidere.model.ParcelableActivity; -import org.mariotaku.twidere.util.MediaLoaderWrapper; import org.mariotaku.twidere.util.MediaLoadingHandler; import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle; @@ -40,9 +37,7 @@ public interface IActivitiesAdapter extends IContentCardAdapter, IGapSuppo @PreviewStyle int getMediaPreviewStyle(); - @NonNull - @Override - MediaLoaderWrapper getMediaLoader(); + boolean shouldUseStarsForLikes(); MediaLoadingHandler getMediaLoadingHandler(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java index 41a50cd76..e759fa460 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java @@ -34,6 +34,7 @@ import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.util.AsyncTwitterWrapper; import org.mariotaku.twidere.util.KeyboardShortcutsHandler; import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback; +import org.mariotaku.twidere.util.LinkCreator; import org.mariotaku.twidere.util.RecyclerViewNavigationHelper; import org.mariotaku.twidere.util.RecyclerViewUtils; import org.mariotaku.twidere.util.Utils; @@ -121,7 +122,9 @@ public abstract class AbsStatusesFragment extends AbsContentListRecyclerVi if (status == null) return false; if (item.getItemId() == R.id.share) { final Intent shareIntent = Utils.createStatusShareIntent(getActivity(), status); - startActivity(Intent.createChooser(shareIntent, getString(R.string.share_status))); + final Intent chooser = Intent.createChooser(shareIntent, getString(R.string.share_status)); + Utils.addCopyLinkIntent(getContext(), chooser, LinkCreator.getTwitterStatusLink(status)); + startActivity(chooser); return true; } return Utils.handleMenuItemClick(getActivity(), AbsStatusesFragment.this, diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java index 7d5eded23..8fb147175 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java @@ -203,7 +203,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem } else { positiveButton.setText(isMyRetweet(status) ? R.string.cancel_retweet : R.string.retweet); } - final String statusLink = LinkCreator.getTwitterStatusLink(status.user_screen_name, status.id).toString(); + final String statusLink = LinkCreator.getTwitterStatusLink(status).toString(); final StatusTextCountView textCountView = (StatusTextCountView) alertDialog.findViewById(R.id.comment_text_count); textCountView.setTextCount(mValidator.getTweetLength(s + " " + statusLink)); } 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 3c02a14be..b003c8e59 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 @@ -288,7 +288,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac final ParcelableStatus status = getStatus(); if (status == null) return null; return new NdefMessage(new NdefRecord[]{ - NdefRecord.createUri(LinkCreator.getTwitterStatusLink(status.user_screen_name, status.id)), + NdefRecord.createUri(LinkCreator.getTwitterStatusLink(status)), }); } }); 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 38b2b6353..8832c8e24 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 @@ -62,7 +62,6 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.internal.widget.ActionBarContainer; import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.Toolbar; -import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -869,9 +868,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener } final MenuItem muteItem = menu.findItem(R.id.mute_user); if (muteItem != null) { - final boolean muting = relationship.isSourceMutingTarget(); - ActionIconDrawable.setMenuHighlight(muteItem, new TwidereMenuInfo(muting)); - muteItem.setTitle(muting ? R.string.unmute : R.string.mute); + muteItem.setChecked(relationship.isSourceMutingTarget()); } final MenuItem filterItem = menu.findItem(R.id.add_to_filter); if (filterItem != null) { @@ -881,7 +878,6 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener } final MenuItem wantRetweetsItem = menu.findItem(R.id.enable_retweets); if (wantRetweetsItem != null) { - wantRetweetsItem.setChecked(relationship.isSourceWantRetweetsFromTarget()); } } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java index fb3c17bd9..9ebe31621 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java @@ -400,7 +400,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList private void setupUserPages() { final Bundle args = getArguments(), tabArgs = new Bundle(); - if (args.containsKey(EXTRA_USER)) { + if (args.containsKey(EXTRA_USER_LIST)) { final ParcelableUserList userList = args.getParcelable(EXTRA_USER_LIST); tabArgs.putLong(EXTRA_ACCOUNT_ID, userList.account_id); tabArgs.putLong(EXTRA_USER_ID, userList.user_id); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembersFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembersFragment.java index ac8eeaec4..af9b81664 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembersFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembersFragment.java @@ -26,17 +26,15 @@ import android.content.IntentFilter; import android.os.AsyncTask; import android.os.Bundle; -import org.mariotaku.twidere.loader.support.CursorSupportUsersLoader; -import org.mariotaku.twidere.loader.support.UserListMembersLoader; -import org.mariotaku.twidere.model.ParcelableUserList; -import org.mariotaku.twidere.util.AsyncTaskUtils; -import org.mariotaku.twidere.util.TwitterAPIFactory; - import org.mariotaku.twidere.api.twitter.Twitter; import org.mariotaku.twidere.api.twitter.TwitterException; import org.mariotaku.twidere.api.twitter.model.UserList; - -import static org.mariotaku.twidere.util.TwitterAPIFactory.getTwitterInstance; +import org.mariotaku.twidere.loader.support.CursorSupportUsersLoader; +import org.mariotaku.twidere.loader.support.UserListMembersLoader; +import org.mariotaku.twidere.model.ParcelableUserList; +import org.mariotaku.twidere.model.SingleResponse; +import org.mariotaku.twidere.util.AsyncTaskUtils; +import org.mariotaku.twidere.util.TwitterAPIFactory; public class UserListMembersFragment extends CursorSupportUsersListFragment { @@ -107,7 +105,7 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment { super.onStop(); } - private class GetUserListTask extends AsyncTask { + private class GetUserListTask extends AsyncTask> { private final long accountId, userId; private final long listId; @@ -123,7 +121,7 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment { } @Override - protected ParcelableUserList doInBackground(final Object... params) { + protected SingleResponse doInBackground(final Object... params) { final Twitter twitter = TwitterAPIFactory.getTwitterInstance(getActivity(), accountId, true); if (twitter == null) return null; try { @@ -136,17 +134,16 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment { list = twitter.showUserList(listName, screenName); } else return null; - return new ParcelableUserList(list, accountId); + return SingleResponse.getInstance(new ParcelableUserList(list, accountId)); } catch (final TwitterException e) { - e.printStackTrace(); - return null; + return SingleResponse.getInstance(e); } } @Override - protected void onPostExecute(final ParcelableUserList result) { + protected void onPostExecute(final SingleResponse result) { if (mUserList != null) return; - mUserList = result; + mUserList = result.getData(); } } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java index 7dd78621b..02f9046d1 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java +++ b/twidere/src/main/java/org/mariotaku/twidere/provider/TwidereDataProvider.java @@ -771,7 +771,6 @@ public final class TwidereDataProvider extends ContentProvider implements Consta final String query = uri.getQueryParameter(QUERY_PARAM_QUERY); final long accountId = ParseUtils.parseLong(uri.getQueryParameter(QUERY_PARAM_ACCOUNT_ID), -1); if (query == null || accountId <= 0) return null; - final ContentResolver resolver = getContentResolver(); final boolean emptyQuery = TextUtils.isEmpty(query); final String queryEscaped = query.replace("_", "^_"); final Cursor[] cursors; @@ -815,12 +814,13 @@ public final class TwidereDataProvider extends ContentProvider implements Consta new Column(CachedUsers.USER_ID, Suggestions.Search.EXTRA_ID).getSQL(), new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL() }; + String queryTrimmed = queryEscaped.startsWith("@") ? queryEscaped.substring(1) : queryEscaped; final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager); final Expression usersSelection = Expression.or( Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"), Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"), Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds))); - final String[] selectionArgs = new String[]{queryEscaped, queryEscaped}; + final String[] selectionArgs = new String[]{queryTrimmed, queryTrimmed}; final String[] order = {CachedUsers.LAST_SEEN, "score", CachedUsers.SCREEN_NAME, CachedUsers.NAME}; final boolean[] ascending = {false, false, true, true}; final OrderBy orderBy = new OrderBy(order, ascending); @@ -831,7 +831,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta final Expression exactUserSelection = Expression.or(Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?", "^")); final Cursor exactUserCursor = mDatabaseWrapper.query(CachedUsers.TABLE_NAME, new String[]{SQLFunctions.COUNT()}, exactUserSelection.getSQL(), - new String[]{queryEscaped}, null, null, null, "1"); + new String[]{queryTrimmed}, null, null, null, "1"); final boolean hasName = exactUserCursor.moveToPosition(0) && exactUserCursor.getInt(0) > 0; exactUserCursor.close(); final MatrixCursor screenNameCursor = new MatrixCursor(Suggestions.Search.COLUMNS); @@ -870,9 +870,11 @@ public final class TwidereDataProvider extends ContentProvider implements Consta new Column(CachedUsers.USER_ID, Suggestions.EXTRA_ID).getSQL(), new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.ICON).getSQL(), }; + final String[] orderBy = {"score", CachedUsers.LAST_SEEN, CachedUsers.SCREEN_NAME, + CachedUsers.NAME}; + final boolean[] ascending = {false, false, true, true}; return query(Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountId), - mappedProjection, where.getSQL(), whereArgs, new OrderBy(new String[]{"score", CachedUsers.LAST_SEEN}, - new boolean[]{false, false}).getSQL()); + mappedProjection, where.getSQL(), whereArgs, new OrderBy(orderBy, ascending).getSQL()); } else if (Suggestions.AutoComplete.TYPE_HASHTAGS.equals(type)) { final Expression where = Expression.likeRaw(new Column(CachedHashtags.NAME), "?||'%'", "^"); final String[] whereArgs = new String[]{queryEscaped}; diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java index 5efa20e87..cf44d8e93 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/AsyncTwitterWrapper.java @@ -878,22 +878,18 @@ public class AsyncTwitterWrapper extends TwitterWrapper { final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true); if (twitter == null) return SingleResponse.getInstance(); try { - final org.mariotaku.twidere.api.twitter.model.Status status = twitter.createFavorite(status_id); - Utils.setLastSeen(mContext, status.getUserMentionEntities(), System.currentTimeMillis()); + final ParcelableStatus status = new ParcelableStatus(twitter.createFavorite(status_id), account_id, false); + Utils.setLastSeen(mContext, status.mentions, System.currentTimeMillis()); final ContentValues values = new ContentValues(); values.put(Statuses.IS_FAVORITE, true); - if (status.isRetweet()) { - values.put(Statuses.FAVORITE_COUNT, status.getRetweetedStatus().getFavoriteCount()); - } else { - values.put(Statuses.FAVORITE_COUNT, status.getFavoriteCount()); - } + values.put(Statuses.FAVORITE_COUNT, status.favorite_count); final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), Expression.or(Expression.equals(Statuses.STATUS_ID, status_id), Expression.equals(Statuses.RETWEET_ID, status_id))); for (final Uri uri : TwidereDataStore.STATUSES_URIS) { mResolver.update(uri, values, where.getSQL(), null); } - return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false)); + return SingleResponse.getInstance(status); } catch (final TwitterException e) { Log.w(LOGTAG, e); return SingleResponse.getInstance(e); @@ -1516,20 +1512,17 @@ public class AsyncTwitterWrapper extends TwitterWrapper { final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true); if (twitter != null) { try { - final org.mariotaku.twidere.api.twitter.model.Status status = twitter.destroyFavorite(status_id); + final ParcelableStatus status = new ParcelableStatus(twitter.destroyFavorite(status_id), account_id, false); final ContentValues values = new ContentValues(); values.put(Statuses.IS_FAVORITE, false); - if (status.isRetweet()) { - values.put(Statuses.FAVORITE_COUNT, status.getRetweetedStatus().getFavoriteCount()); - } else { - values.put(Statuses.FAVORITE_COUNT, status.getFavoriteCount()); - } + values.put(Statuses.FAVORITE_COUNT, status.favorite_count - 1); final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id), - Expression.or(Expression.equals(Statuses.STATUS_ID, status_id), Expression.equals(Statuses.RETWEET_ID, status_id))); + Expression.or(Expression.equals(Statuses.STATUS_ID, status_id), + Expression.equals(Statuses.RETWEET_ID, status_id))); for (final Uri uri : TwidereDataStore.STATUSES_URIS) { mResolver.update(uri, values, where.getSQL(), null); } - return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false)); + return SingleResponse.getInstance(status); } catch (final TwitterException e) { return SingleResponse.getInstance(e); } @@ -1730,6 +1723,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper { if (status != null || exception.getErrorCode() == HttpResponseCode.NOT_FOUND) { final ContentValues values = new ContentValues(); values.put(Statuses.MY_RETWEET_ID, -1); + if (status != null) { + values.put(Statuses.RETWEET_COUNT, status.retweet_count - 1); + } for (final Uri uri : TwidereDataStore.STATUSES_URIS) { mResolver.delete(uri, Statuses.STATUS_ID + " = " + status_id, null); mResolver.update(uri, values, Statuses.MY_RETWEET_ID + " = " + status_id, null); @@ -2585,9 +2581,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper { return SingleResponse.getInstance(); } try { - final org.mariotaku.twidere.api.twitter.model.Status status = twitter.retweetStatus(status_id); - Utils.setLastSeen(mContext, status.getUserMentionEntities(), System.currentTimeMillis()); - return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false)); + final ParcelableStatus status = new ParcelableStatus(twitter.retweetStatus(status_id), account_id, false); + Utils.setLastSeen(mContext, status.mentions, System.currentTimeMillis()); + return SingleResponse.getInstance(status); } catch (final TwitterException e) { return SingleResponse.getInstance(e); } @@ -2606,9 +2602,10 @@ public class AsyncTwitterWrapper extends TwitterWrapper { protected void onPostExecute(final SingleResponse result) { mCreatingRetweetIds.remove(account_id, status_id); if (result.hasData()) { - final ContentValues values = new ContentValues(); final ParcelableStatus status = result.getData(); + final ContentValues values = new ContentValues(); values.put(Statuses.MY_RETWEET_ID, status.id); + values.put(Statuses.RETWEET_COUNT, status.retweet_count); final Expression where = Expression.or( Expression.equals(Statuses.STATUS_ID, status_id), Expression.equals(Statuses.RETWEET_ID, status_id) @@ -2625,7 +2622,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper { // END HotMobi - bus.post(new StatusRetweetedEvent(status)); Utils.showOkMessage(mContext, R.string.status_retweeted, false); } else { diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/LinkCreator.java b/twidere/src/main/java/org/mariotaku/twidere/util/LinkCreator.java index c95a6d340..242b61b7a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/LinkCreator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/LinkCreator.java @@ -22,6 +22,7 @@ package org.mariotaku.twidere.util; import android.net.Uri; import org.mariotaku.twidere.Constants; +import org.mariotaku.twidere.model.ParcelableStatus; /** * Created by mariotaku on 15/3/14. @@ -83,4 +84,8 @@ public class LinkCreator implements Constants { builder.appendPath(screenName); return builder.build(); } + + public static Uri getTwitterStatusLink(ParcelableStatus status) { + return getTwitterStatusLink(status.user_screen_name, status.id); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/SwipeDismissListViewTouchListener.java b/twidere/src/main/java/org/mariotaku/twidere/util/SwipeDismissListViewTouchListener.java new file mode 100644 index 000000000..34745a47d --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/util/SwipeDismissListViewTouchListener.java @@ -0,0 +1,403 @@ +/* + * 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.util; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; +import android.widget.AbsListView; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A {@link View.OnTouchListener} that makes the list items in a {@link ListView} + * dismissable. {@link ListView} is given special treatment because by default it handles touches + * for its list items... i.e. it's in charge of drawing the pressed state (the list selector), + * handling list item clicks, etc. + *

+ *

After creating the listener, the caller should also call + * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing + * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is + * already assigned, the caller should still pass scroll changes through to this listener. This will + * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view + * scrolling.

+ *

+ *

Example usage:

+ *

+ *

+ * SwipeDismissListViewTouchListener touchListener =
+ *         new SwipeDismissListViewTouchListener(
+ *                 listView,
+ *                 new SwipeDismissListViewTouchListener.OnDismissCallback() {
+ *                     public void onDismiss(ListView listView, int[] reverseSortedPositions) {
+ *                         for (int position : reverseSortedPositions) {
+ *                             adapter.remove(adapter.getItem(position));
+ *                         }
+ *                         adapter.notifyDataSetChanged();
+ *                     }
+ *                 });
+ * listView.setOnTouchListener(touchListener);
+ * listView.setOnScrollListener(touchListener.makeScrollListener());
+ * 
+ *

+ *

This class Requires API level 12 or later due to use of {@link + * ViewPropertyAnimator}.

+ */ +public class SwipeDismissListViewTouchListener implements View.OnTouchListener { + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + + // Fixed properties + private ListView mListView; + private DismissCallbacks mCallbacks; + private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero + + // Transient properties + private List mPendingDismisses = new ArrayList<>(); + private int mDismissAnimationRefCount = 0; + private float mDownX; + private float mDownY; + private boolean mSwiping; + private int mSwipingSlop; + private VelocityTracker mVelocityTracker; + private int mDownPosition; + private View mDownView; + private boolean mPaused; + + /** + * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client + * about a successful dismissal of one or more list item positions. + */ + public interface DismissCallbacks { + /** + * Called to determine whether the given position can be dismissed. + */ + boolean canDismiss(int position); + + /** + * Called when the user has indicated they she would like to dismiss one or more list item + * positions. + * + * @param listView The originating {@link ListView}. + * @param reverseSortedPositions An array of positions to dismiss, sorted in descending + * order for convenience. + */ + void onDismiss(ListView listView, int[] reverseSortedPositions); + } + + /** + * Constructs a new swipe-to-dismiss touch listener for the given list view. + * + * @param listView The list view whose items should be dismissable. + * @param callbacks The callback to trigger when the user has indicated that she would like to + * dismiss one or more list items. + */ + public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) { + ViewConfiguration vc = ViewConfiguration.get(listView.getContext()); + mSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = listView.getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + mListView = listView; + mCallbacks = callbacks; + } + + /** + * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures. + * + * @param enabled Whether or not to watch for gestures. + */ + public void setEnabled(boolean enabled) { + mPaused = !enabled; + } + + /** + * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link + * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}. + * If a scroll listener is already assigned, the caller should still pass scroll changes through + * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is + * paused during list view scrolling.

+ * + * @see SwipeDismissListViewTouchListener + */ + public AbsListView.OnScrollListener makeScrollListener() { + return new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView absListView, int scrollState) { + setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + + @Override + public void onScroll(AbsListView absListView, int i, int i1, int i2) { + } + }; + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (mViewWidth < 2) { + mViewWidth = mListView.getWidth(); + } + + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + if (mPaused) { + return false; + } + + // TODO: ensure this is a finger, and set a flag + + // Find the child view that was touched (perform a hit test) + Rect rect = new Rect(); + int childCount = mListView.getChildCount(); + int[] listViewCoords = new int[2]; + mListView.getLocationOnScreen(listViewCoords); + int x = (int) motionEvent.getRawX() - listViewCoords[0]; + int y = (int) motionEvent.getRawY() - listViewCoords[1]; + View child; + for (int i = 0; i < childCount; i++) { + child = mListView.getChildAt(i); + child.getHitRect(rect); + if (rect.contains(x, y)) { + mDownView = child; + break; + } + } + + if (mDownView != null) { + mDownX = motionEvent.getRawX(); + mDownY = motionEvent.getRawY(); + mDownPosition = mListView.getPositionForView(mDownView); + if (mCallbacks.canDismiss(mDownPosition)) { + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(motionEvent); + } else { + mDownView = null; + } + } + return false; + } + + case MotionEvent.ACTION_CANCEL: { + if (mVelocityTracker == null) { + break; + } + + if (mDownView != null && mSwiping) { + // cancel + mDownView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mDownX = 0; + mDownY = 0; + mDownView = null; + mDownPosition = ListView.INVALID_POSITION; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_UP: { + if (mVelocityTracker == null) { + break; + } + + float deltaX = motionEvent.getRawX() - mDownX; + mVelocityTracker.addMovement(motionEvent); + mVelocityTracker.computeCurrentVelocity(1000); + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + boolean dismiss = false; + boolean dismissRight = false; + if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { + dismiss = true; + dismissRight = deltaX > 0; + } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX && mSwiping) { + // dismiss only if flinging in the same direction as dragging + dismiss = (velocityX < 0) == (deltaX < 0); + dismissRight = mVelocityTracker.getXVelocity() > 0; + } + if (dismiss && mDownPosition != ListView.INVALID_POSITION) { + // dismiss + final View downView = mDownView; // mDownView gets null'd before animation ends + final int downPosition = mDownPosition; + ++mDismissAnimationRefCount; + mDownView.animate() + .translationX(dismissRight ? mViewWidth : -mViewWidth) + .alpha(0) + .setDuration(mAnimationTime) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + performDismiss(downView, downPosition); + } + }); + } else { + // cancel + mDownView.animate() + .translationX(0) + .alpha(1) + .setDuration(mAnimationTime) + .setListener(null); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + mDownX = 0; + mDownY = 0; + mDownView = null; + mDownPosition = ListView.INVALID_POSITION; + mSwiping = false; + break; + } + + case MotionEvent.ACTION_MOVE: { + if (mVelocityTracker == null || mPaused) { + break; + } + + mVelocityTracker.addMovement(motionEvent); + float deltaX = motionEvent.getRawX() - mDownX; + float deltaY = motionEvent.getRawY() - mDownY; + if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { + mSwiping = true; + mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); + mListView.requestDisallowInterceptTouchEvent(true); + + // Cancel ListView's touch (un-highlighting the item) + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | + (motionEvent.getActionIndex() + << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); + mListView.onTouchEvent(cancelEvent); + cancelEvent.recycle(); + } + + if (mSwiping) { + mDownView.setTranslationX(deltaX - mSwipingSlop); + mDownView.setAlpha(Math.max(0f, Math.min(1f, + 1f - 2f * Math.abs(deltaX) / mViewWidth))); + return true; + } + break; + } + } + return false; + } + + class PendingDismissData implements Comparable { + public int position; + public View view; + + public PendingDismissData(int position, View view) { + this.position = position; + this.view = view; + } + + @Override + public int compareTo(@NonNull PendingDismissData other) { + // Sort by descending position + return other.position - position; + } + } + + private void performDismiss(final View dismissView, final int dismissPosition) { + // Animate the dismissed list item to zero-height and fire the dismiss callback when + // all dismissed list item animations have completed. This triggers layout on each animation + // frame; in the future we may want to do something smarter and more performant. + + final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); + final int originalHeight = dismissView.getHeight(); + + ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + --mDismissAnimationRefCount; + if (mDismissAnimationRefCount == 0) { + // No active animations, process all pending dismisses. + // Sort by descending position + Collections.sort(mPendingDismisses); + + int[] dismissPositions = new int[mPendingDismisses.size()]; + for (int i = mPendingDismisses.size() - 1; i >= 0; i--) { + dismissPositions[i] = mPendingDismisses.get(i).position; + } + mCallbacks.onDismiss(mListView, dismissPositions); + + // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss + // animation with a stale position + mDownPosition = ListView.INVALID_POSITION; + + ViewGroup.LayoutParams lp; + for (PendingDismissData pendingDismiss : mPendingDismisses) { + // Reset view presentation + pendingDismiss.view.setAlpha(1f); + pendingDismiss.view.setTranslationX(0); + lp = pendingDismiss.view.getLayoutParams(); + lp.height = originalHeight; + pendingDismiss.view.setLayoutParams(lp); + } + + // Send a cancel event + long time = SystemClock.uptimeMillis(); + MotionEvent cancelEvent = MotionEvent.obtain(time, time, + MotionEvent.ACTION_CANCEL, 0, 0, 0); + mListView.dispatchTouchEvent(cancelEvent); + + mPendingDismisses.clear(); + } + } + }); + + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + lp.height = (Integer) valueAnimator.getAnimatedValue(); + dismissView.setLayoutParams(lp); + } + }); + + mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView)); + animator.start(); + } +} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 2c38dddc2..082c97a0b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -134,6 +134,7 @@ import org.mariotaku.sqliteqb.library.query.SQLSelectQuery; import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; +import org.mariotaku.twidere.activity.CopyLinkActivity; import org.mariotaku.twidere.activity.support.AccountSelectorActivity; import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity; import org.mariotaku.twidere.activity.support.MediaViewerActivity; @@ -197,6 +198,7 @@ import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUserList; +import org.mariotaku.twidere.model.ParcelableUserMention; import org.mariotaku.twidere.model.PebbleMessage; import org.mariotaku.twidere.provider.TwidereDataStore; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; @@ -1181,7 +1183,7 @@ public final class Utils implements Constants { } public static String getStatusShareText(@NonNull final Context context, @NonNull final ParcelableStatus status) { - final Uri link = LinkCreator.getTwitterStatusLink(status.user_screen_name, status.id); + final Uri link = LinkCreator.getTwitterStatusLink(status); return context.getString(R.string.status_share_text_format_with_link, status.text_plain, link.toString()); } @@ -1631,6 +1633,15 @@ public final class Utils implements Constants { return textView; } + public static boolean setLastSeen(Context context, ParcelableUserMention[] entities, long time) { + if (entities == null) return false; + boolean result = false; + for (ParcelableUserMention entity : entities) { + result |= setLastSeen(context, entity.id, time); + } + return result; + } + public static boolean setLastSeen(Context context, UserMentionEntity[] entities, long time) { if (entities == null) return false; boolean result = false; @@ -3379,11 +3390,20 @@ public final class Utils implements Constants { addIntentToMenu(context, shareSubMenu, shareIntent, MENU_GROUP_STATUS_SHARE); } else { final Intent shareIntent = createStatusShareIntent(context, status); - shareItem.setIntent(Intent.createChooser(shareIntent, context.getString(R.string.share_status))); + final Intent chooserIntent = Intent.createChooser(shareIntent, context.getString(R.string.share_status)); + addCopyLinkIntent(context, chooserIntent, LinkCreator.getTwitterStatusLink(status)); + shareItem.setIntent(chooserIntent); } } + public static void addCopyLinkIntent(Context context, Intent chooserIntent, Uri uri) { + final Intent copyLinkIntent = new Intent(context, CopyLinkActivity.class); + copyLinkIntent.setData(uri); + final Intent[] alternateIntents = {copyLinkIntent}; + chooserIntent.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alternateIntents); + } + private static boolean isMyStatus(ParcelableStatus status) { if (isMyRetweet(status)) return true; return status.account_id == status.user_id; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityTitleSummaryViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityTitleSummaryViewHolder.java index 0d6c700d3..7e37dcc35 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityTitleSummaryViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityTitleSummaryViewHolder.java @@ -102,14 +102,28 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O break; } case Activity.ACTION_FAVORITE: { - activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); - activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); - if (byFriends) { - titleView.setText(getTitleStringByFriends(R.string.activity_by_friends_favorite, - R.string.activity_by_friends_favorite_multi, activity.sources, activity.target_statuses)); + if (adapter.shouldUseStarsForLikes()) { + activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP); + + if (byFriends) { + titleView.setText(getTitleStringByFriends(R.string.activity_by_friends_favorite, + R.string.activity_by_friends_favorite_multi, activity.sources, activity.target_statuses)); + } else { + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorite, + R.string.activity_about_me_favorite_multi, activity.sources)); + } } else { - titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorite, - R.string.activity_about_me_favorite_multi, activity.sources)); + activityTypeView.setImageResource(R.drawable.ic_activity_action_like); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); + + if (byFriends) { + titleView.setText(getTitleStringByFriends(R.string.activity_by_friends_like, + R.string.activity_by_friends_like_multi, activity.sources, activity.target_statuses)); + } else { + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_like, + R.string.activity_about_me_like_multi, activity.sources)); + } } displayUserProfileImages(activity.sources); summaryView.setText(activity.target_statuses[0].text_unescaped); @@ -135,10 +149,17 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O showNotSupported(); return; } - activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); - activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); - titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorited_retweet, - R.string.activity_about_me_favorited_retweet_multi, activity.sources)); + if (adapter.shouldUseStarsForLikes()) { + activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP); + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorited_retweet, + R.string.activity_about_me_favorited_retweet_multi, activity.sources)); + } else { + activityTypeView.setImageResource(R.drawable.ic_activity_action_like); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_liked_retweet, + R.string.activity_about_me_liked_retweet_multi, activity.sources)); + } displayUserProfileImages(activity.sources); summaryView.setText(activity.target_statuses[0].text_unescaped); summaryView.setVisibility(View.VISIBLE); @@ -177,10 +198,17 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O showNotSupported(); return; } - activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); - activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); - titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_liked_mention, - R.string.activity_about_me_liked_mention_multi, activity.sources)); + if (adapter.shouldUseStarsForLikes()) { + activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP); + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorited_mention, + R.string.activity_about_me_favorited_mention_multi, activity.sources)); + } else { + activityTypeView.setImageResource(R.drawable.ic_activity_action_like); + activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP); + titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_liked_mention, + R.string.activity_about_me_liked_mention_multi, activity.sources)); + } displayUserProfileImages(activity.sources); summaryView.setText(activity.target_statuses[0].text_unescaped); summaryView.setVisibility(View.VISIBLE); diff --git a/twidere/src/main/res-svg2png/drawable-hdpi/ic_activity_action_favorite.png b/twidere/src/main/res-svg2png/drawable-hdpi/ic_activity_action_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b71586cc6815a2eb4080f67deddc9c76219f75 GIT binary patch literal 696 zcmV;p0!RIcP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02rG902rGANp*vi0006H zNklLEQu$1QiDz6a^8I%|#Hj zxGL)4;4BV8|A0;of({)zbgUxcP^j;LBV1zACcQVULJz#0nst;{LGR~ib02vFY4Re7ZD7NFT)6yqfA zVjtyl?siz~HTM33imZ~ux&Xb=TGON4rD;UxzUpuV)C-_)c^q^G1oI4y0_Mrnvr}VV|0000|k1|%Oc%$NbB6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_HUsu3@kvn$XBD8Z8M=jA5L~c#IY^qbz$3Dlfk9$62s3(?HctU+IpXQ!7*cU7IYEMTae~MNF$1BX5C8wy zvnx#HIU?tv!z8+o@rZRpg_^SRV_uW5Y7Sbm9kC4&j9PzKR@G?*{AJxF!(uXn-NTpt zk%Y~!{|trP0^6Akxdpy67qT(he`6?Ay^vVH!E@7t0v?YV+bh$5FnGi=3%q97$RcoC zKu}xt!UVq~YA0KqgDnO79N83Itk~Fu8s_z>opN+&l?jNyAazAR+UcBwjsrV;CWGyI z=bmPs7c)+O?JN}FmX$r?)YEKt;i98_N8trgE2|e;hJ02WNAh1hn8AJE&__N;c1Q0E znk5AnSc15Y`6<*nKE4p?v8B18tG>Z2SjzGl`?@S$ax*d*QoXIUtTmWl zJbb%DWhO^P9?K)sD?a89$Il#H&al3D0jG^vM}NRULxujSLP1A-JQxjL{bN|1XKM25 ziAI5s$~rRvPrfFrSK_RFO1BJMtU1mGRynM3Te6MSLB@Ygaz|G1;|yQDcC%9)st=rk z&Zet{G8-)U$LP#;WzSRQByO2gS`B?GmwK*mcE|{Imi~F7_aBc_43$6<|}nvm=i{v$V+WF#mZ{A%Q&#n2Jlki!@CgW*Y$n1UF?ql#5g45fuklLFYb z)(Y?(5%Q>E4rmcxJW;TJwd#U{jEjCdwk^`U$#`V0!ylFodjlh*eFw7{FC0iy5N%4) zuw=-Q6gbZiWg%+YA=UVUiM6enL1XFL2FX6tjsP!nvWvMyBshk-F}rPJG&w26w7^H*p^yK}#wC*^9riSFY%Ms@bexG@<(s6!Io7tX zoGadoF|5>dSkttmOns%@hK6Te0SD7Kjb&yrIJCWV`h1$d=Hr2m$VP{)^%8E3D;XG691;re2#US~1~`MKtDnm{r-UW|dnmGW literal 0 HcmV?d00001 diff --git a/twidere/src/main/res-svg2png/drawable-mdpi/ic_activity_action_like.png b/twidere/src/main/res-svg2png/drawable-mdpi/ic_activity_action_like.png new file mode 100644 index 0000000000000000000000000000000000000000..616d6198b46dbfee9dbb9d115d12738639f0eb90 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj3dtTpz6=aiY77hwEes65fIssua7vue+_YKZZs-YzX0DH` zn$I(3G4R!h%~{yQn*JejLkpLf&cXD9J_>aXjStluPL?S9aW_n|ZwS#aIXjulj-gOx zmxA#w1_NGu=4DT#9nv^B+nQyb@RdAJZ4z_P-O06u<%tr%5^q0))IQA6YwW8{6`ar?VL={%6+R$+zW+%!HThM{)uliaWgF zH~Ax=`1j0_BMKKL-FEQibJne7KXT6D@cF>KJPgyeNjc~kM)d}XGB7B5hd0ezm?8xX OSq4v6KbLh*2~7Z_7^VLJ literal 0 HcmV?d00001 diff --git a/twidere/src/main/res-svg2png/drawable-xhdpi/ic_activity_action_favorite.png b/twidere/src/main/res-svg2png/drawable-xhdpi/ic_activity_action_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..dd5d17e185a3e7eb32a9cc7ce6967449b732605a GIT binary patch literal 860 zcmV-i1Ec(jP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02rG902rGANp*vi0008B zNklAr^aN~?96+!Gnub`y=U#d`F`K~{!Sp^i!c9MN@O0M z%jM#UL?W0>Cf{T|BPiKG)4+tx2LvTrzYS2fnLB&kg6aX6ZKHVLVHlb)wPOOLN!2nzQ*KHH^4RU3}gXn z_Fj0ORTL#-nE;d=<1`p%Wh*s%CndwgGMfDv$nEP>n_f=25JijJ>PHygdRzjFf6fEc zyQ7ES!M256!ws@WF$U|un~nj_DqFgytQeAl;)|Ap7(|w^B#NN;C#7X5zin%zgiD>n zr5>>DabSY`7IKwtD1RViCI*RHkhtIpt5mY`lv+EX{FTt59wgEraYUBkv?@cWKD0ym zM=5V5Kq3ng`&_g{x5mdn7nFaM`dS4f-kC(bz*@0Mb2F6xaHJRFe7r8u`pY=;)9Gfe zT_6F=XW1d33M8NwgMi4l3~@&ZpgK2UDHBMb0cVDtyaAE^Z|lsUWS_m~$|@}mLXv!z z(rgM?<@?cU*`>~|@;V~) m5}}rccMf)6eDTE>kH{B{L%=k@AhC1+0000004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02rG902rGANp*vi0007G zNkl>|84|{NulcYU3I-rDda1n`85^^E_0P5Yyk6c{jfaKt? zQT9MiTZ)5&8wUqDNRlLnO_EyQsb_0b>z#St+1*j|t#9vJ?>sZlXJ+1cXI{s#va+(W zDwnd^Yz&Tuh>wJc7jhLFtixpl?tz=&N_v!mFX0rt@4D`LAz)nk;9L4_(LE1QOIOPKtb=sTH$o=hQ(!WTy2MC{Kcj)Y+) zI;so3|Dg?lAl?CyrUQ9Zk&=IgTao;WY=EayJ0P9`agN)8B0z}b+mZaeN|2}83LqYe z0Fg^x`91SEt!ai}C(j1Z2oWmzPyl%lNAwFN!dPuuRp-1`0D^d>CALT56eqUF9Z3Eu zZ2O*$H;^Eb1Q81;enHdbO-I|Frd32hWB{>;+cvniKr!*Vu~+sZRZ@VwFShs({$Vb0000< KMNUMnLSTaEwL(b% literal 0 HcmV?d00001 diff --git a/twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_favorite.png b/twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..d1aaf4f68c7ebc7322963a9553a4845a1c7887a6 GIT binary patch literal 1258 zcmV004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02rG902rGANp*vi000C* zNklMSxd-GrI_P7E4{9#DW5TMt>xLDQdl6`;k^1+6oEPLab_fd0vVz+o#uu#_~4#sXh; zvf~cD1&P8sIqV~$1_TQsztdgh65m6<-xhQTS`0uPJMi!Uf@Q=khV#C#TF`E&6(^hq z%*HSR8nBt8g$EEUB89@vM6jD3dT1=jZuqY8D?f|+U_J4?yvwTev16}#3n~@bMN-d> zz3Kt=`E;-RfG|WlP^_+?iB`0c^s>(p2PS)D`U&WAQ$4}6VA8m$?o`^4h0Z{ypmV1B z41fO4|%BS}hn-2%@?E86e+?GG>u1gI);`gktjd#jY*z zT?V>Kbfa+F$1seqx~@OgatrCz0I86Cf^LQNe6$USA!Pt*1l8@(6;%djoZ(CtyQt*G zAs}S|=?YmL=qkzJ+=F%j&TJ)(RD3=NkbVNvU7i9YC4n;pJ!&P4 zyPB*Ph;ZjoobfqQ4FJqlry6whgc+=94AwNo&fDv?Cd%-;?S^qr0WqYvfb@!UxTPM! z$*?n?aJpev-ZDD+gu|<6(W=}q?h7FJqFRp~cG|6m-SQL79?!Dt@KPFr1SqX}FPnn| z$dBW5Er9^U%OYt7NGvIz64(a3oJ0@+HTyV|ZVmzX3VNBatLZS8V>!{AMl@{|e30yB9AUr@jioHdK(`7BUoB6t&_VLiap)c_}w>c1O z5L{Y=YaYPea6G7->v0O z3;k&0sRIqWmFUNmEIw!*52u?x=3!(}V}L}%?j&?0PePR_QKCeN5+zEMC~;o?01whX UHNGF*i2wiq07*qoM6N<$g7_3GApigX literal 0 HcmV?d00001 diff --git a/twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_like.png b/twidere/src/main/res-svg2png/drawable-xxhdpi/ic_activity_action_like.png new file mode 100644 index 0000000000000000000000000000000000000000..152adbf0eeaf0d449164630d632fbab6d8442bb3 GIT binary patch literal 1134 zcmV-!1d;oRP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L02rG902rGANp*vi000BU zNkl>nk z{`P&$`&iF&a&mHVa&mHVa&mGKS4NlPQmNF8Lo@$e%49MN!u|ss8u{k}{4XUM5CYzh zLl-Ou+glZUMFi+G>;-Hb4^&k2zY~XxuufQO75s__&|o5`8`wuK48zHMKEG7u{{yUDpqA`|9fSXu75`dG5Q5*$!3Vzuol=W zSd*}SYLgDBb`}{$I280WEUT&WD{$i*X!8Fab{vq_REoHY9%_W+vF|{mf2y)~RNH`B zOAqxXy%ywteVjvym!LsQlH*2B8{`0i_;#TlB`NC0Gc`({Ct> zP}V{~SxG>Fg@6J{Kus0`YLW!BVj-XvNkH=!0-AS#>Ii7oLO`>UfF>*iG${#a$}7() z+oGAxrh1hC@%)KRrD#mYXnxo$&mpH>H0?Sf4v1F=o>&0rzRHr5>Wtz*%9BO{b5G?0 zrBw}D6$rzS=1~r(6i^>Z&$P+~4H0gV_sT1zY10JYjHq6u42XNQ9&YT?lr>+Ca$%%} zLsYdzRJA#rcBcRiz0lK9*XsSKcLk&`fJAFpZ3zc-6L6kIy|^Lf&HGi7E(00@oCk3n z!~(?a(G}Rogl5bAah>>a9iXbMqN+W|=^3xQuAT(Z3+suxqeB!BLs|x;aj(2m(2@W+ zr=)hA`BG7F5@{L|&GB?dZpZT_qcaSNy78lc^IOuXFCC>JNChrAMQO*rj%e!)iQ1f# zfb&a-b6;1~Rgh@&0uS8ENC`xxZbh)CIF$zO`*!sI@^gF2FwJf7jPb%h}8%nhBOUGUvYZ=Kagn1K>M2C zn2OUFAcph}klx|+0&MGszl%I{_+&ChqktIFPe7ti56*h)o0U$ zoORFf(BX&Y^jGf|@b@52PEJlvPEJlvPEM)TABYbZVN{VTrvLx|07*qoM6N<$g7S>@ Ad;kCd literal 0 HcmV?d00001 diff --git a/twidere/src/main/res-svg2png/drawable-xxxhdpi/ic_activity_action_favorite.png b/twidere/src/main/res-svg2png/drawable-xxxhdpi/ic_activity_action_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e8fb55ec80a3e2643ed65e1a6c4837afa793b2 GIT binary patch literal 1616 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 literal 0 HcmV?d00001 diff --git a/twidere/src/main/res/drawable-hdpi/ic_activity_action_favorite.png b/twidere/src/main/res/drawable-hdpi/ic_activity_action_favorite.png deleted file mode 100755 index 6d5a9b300566580d5efc1111081c725eec67f296..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 360 zcmV-u0hj)XP)m z{jS6R$N#pw=aK$05+w8+=+c#YgxIkoB)6z?Qk6S+nR7D7D>uk;mX#a4bM~gk5yKSI zqiAAk>M;b_V%idv6QZQaQ=v|a9s{PVxn#|h0XbgK$xKZ>0X}_meoBiTH0{{00001trM%q`+$S#=@#sJeDl zRoi*i$=v(y89w!yY#eNQSb4GD;mMNLugbCxPu#WmOhM+OQ!T9Jtl8H@?&zi*YP%%o zZ(i|6GdIQQP^aoNxeXCTN`b~QO21YmIexk04jm{M1%sGm%_3qNENnkOn?@BZ1B=nRBqq@X#bUM0va}$EjRipw z#3+iO3%)W`cHO3)bG$bQdmgFhpCLs`QDTohMTaEMJd;#Z$kUWpG>CCS(+x32fgGQ5 ziUMbRDk*$S6Ev-`u|v>~!o?6z1U)gNFtJX%b%lpME@^j3pTa_#ZqjmtM}kRa$+E&G z1rE8Un`;g!u*nKpW|<^`r|+@M0jHdEO9NL8ZaL?a1D4f}1SP^!BB4(B>=TB4e07J% z7Js#cr=PGy16K`}^gHIc$HhJKGQ$*C*t%j$<`|`djS8c(!yqS^I$==u=%;{*0{wDB z3KJ>0Vigmsa>X$wj^&CwOx(#06PTKi3l=f8C>QL~>4FP7?aBo;zItGd7%|p(;H#SK zF~TcBMaJ}jF^UAeGAvtUXsI%%Hq23_B_msG^X`!i;_8Ap8$9xETec|Ebi}xRz&J-V zm1TzkUZ}Gm11wPIg#p=MhCIFu;ge@ZJv|Y5168l@b9yTz$^ZZW07*qoM6N<$f{>rQ AhX4Qo diff --git a/twidere/src/main/res/drawable-xxhdpi/ic_activity_action_favorite.png b/twidere/src/main/res/drawable-xxhdpi/ic_activity_action_favorite.png deleted file mode 100755 index fe555ff1196cecbe1edc214442a6355f1cf3d8e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmV;S0$%-zP)*s0LSs)7?j3KF5~7Tqm*PzxhO6UlEe zr1U^GLGA>}Ryv@a2klA+Z17-1&ZCNGRdO1$M4OefDCUA_7Zl4$4Dj55oI@7-JhxAl zoI(q+w8$x}63eQbK{*j(iBK*lFhx95N<9iVBc3w~lxp;mNS{&*7k^0PH!h_PIkfVc zR&rz-ZoE{|Ko|XtFvAi7b~z=&TSPczmjFx5FhW0FG*F2bx88~-dKqS#1=iT%h+FJ% z%Mm-QvA{IL^wOkw_&|tM3sIx^C}o>e+NM;AQG}mV@l&MNk;8XV;Tt)6E4~uJP7#Lm zK6DetE@8U$e$;b<9Zsm1HTc-aw0(TC4li4nw1rp6A&*5&StL)%Ba3lN7$-}n(8n#w zxTQ~K(abqkozpDysNoPR4ylnn*u;uW*@IjzvEnbevIBKkRVODiChvUkv*A335l#wMj0z4QX-T1L_7zy=|9>y zAfAshi3wu4Vib>};9-<2VwsRh{Ng1)KE;3!KQH~1Ie56{sUV$7fKGxub&W@+&`7jv z#>m$z$Y+deqBY7ChIzD3rQV22)_F86Q~1HX5Z$=+4!Gzh#JyFSftxenhDi!#0fkI* z1DxTO2~-0Cs+0t(2msYGfew0bDM`5Kp+gzUP=+#;p?CNPU((leseSm100000NkvXX Hu0mjfm=`PE diff --git a/twidere/src/main/res/layout/list_item_compose_account.xml b/twidere/src/main/res/layout/list_item_compose_account.xml new file mode 100644 index 000000000..acc025d68 --- /dev/null +++ b/twidere/src/main/res/layout/list_item_compose_account.xml @@ -0,0 +1,43 @@ + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/menu/menu_user_profile.xml b/twidere/src/main/res/menu/menu_user_profile.xml index fb80c3467..f371d91f4 100644 --- a/twidere/src/main/res/menu/menu_user_profile.xml +++ b/twidere/src/main/res/menu/menu_user_profile.xml @@ -51,6 +51,7 @@ android:title="@string/enable_retweets" /> %1$s and %2$s favorited your tweet. %s favorited your retweet. %1$s and %2$s favorited your retweet. + %s liked your tweet. + %1$s and %2$s liked your tweet. + %s liked your retweet. + %1$s and %2$s liked your retweet. %s is following you. %1$s and %2$s are following you. %s retweeted your tweet. @@ -296,6 +300,8 @@ %1$s and %2$s added you to their lists. %1$s favorited %2$s\'s tweet. %1$s favorited %2$s and %3$s\'s tweet. + %1$s liked %2$s\'s tweet. + %1$s liked %2$s and %3$s\'s tweet. %1$s is following %2$s. %1$s is following %2$s and %3$s. %1$s retweeted %2$s\'s tweet. @@ -811,4 +817,6 @@ Use special search terms to improve search results like exclude retweets I want my stars back! Show use favorite (star) instead of like (heart) + Copy link + Link copied to clipboard \ No newline at end of file diff --git a/twidere/src/main/svg/drawable/ic_activity_action_favorite-mdpi.svg b/twidere/src/main/svg/drawable/ic_activity_action_favorite-mdpi.svg new file mode 100644 index 000000000..fa3a669f6 --- /dev/null +++ b/twidere/src/main/svg/drawable/ic_activity_action_favorite-mdpi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/twidere/src/main/svg/drawable/ic_activity_action_like-mdpi.svg b/twidere/src/main/svg/drawable/ic_activity_action_like-mdpi.svg new file mode 100644 index 000000000..97d0679f0 --- /dev/null +++ b/twidere/src/main/svg/drawable/ic_activity_action_like-mdpi.svg @@ -0,0 +1,12 @@ + + + + Artboard + Created with Sketch. + + + + + + + \ No newline at end of file