improved auto complete
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -341,6 +341,10 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.CopyLinkActivity"
|
||||
android:label="@string/copy_link"
|
||||
android:theme="@style/Theme.Twidere.Dark.NoDisplay" />
|
||||
<activity
|
||||
android:name=".activity.support.RequestPermissionsActivity"
|
||||
android:label="@string/permissions_request"
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <i>initialCapacity</i> 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-<code>null</code> collection of <code>int</code>s
|
||||
* to add
|
||||
* @throws NullPointerException if <i>that</i> is <code>null</code>
|
||||
* @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[])}
|
||||
* <p/>
|
||||
* 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)}
|
||||
* <p/>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.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();
|
||||
}
|
||||
}
|
|
@ -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<Cursor>, 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);
|
||||
|
|
|
@ -58,7 +58,8 @@ import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder;
|
|||
* Created by mariotaku on 15/1/3.
|
||||
*/
|
||||
public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<ViewHolder> implements Constants,
|
||||
IActivitiesAdapter<Data>, IStatusViewHolder.StatusClickListener, OnLinkClickListener, ActivityTitleSummaryViewHolder.ActivityClickListener {
|
||||
IActivitiesAdapter<Data>, 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<Data> 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<Data> 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<Data> extends LoadMoreSupportAdapter<
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldUseStarsForLikes() {
|
||||
return mShouldUseStarsForLikes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserProfileClick(IStatusViewHolder holder, int position) {
|
||||
final Context context = getContext();
|
||||
|
|
|
@ -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<Data> extends IContentCardAdapter, IGapSuppo
|
|||
@PreviewStyle
|
||||
int getMediaPreviewStyle();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
MediaLoaderWrapper getMediaLoader();
|
||||
boolean shouldUseStarsForLikes();
|
||||
|
||||
MediaLoadingHandler getMediaLoadingHandler();
|
||||
}
|
||||
|
|
|
@ -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<Data> 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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Object, Object, ParcelableUserList> {
|
||||
private class GetUserListTask extends AsyncTask<Object, Object, SingleResponse<ParcelableUserList>> {
|
||||
|
||||
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<ParcelableUserList> 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<ParcelableUserList> result) {
|
||||
if (mUserList != null) return;
|
||||
mUserList = result;
|
||||
mUserList = result.getData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<ParcelableStatus> 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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.util;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.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.
|
||||
* <p/>
|
||||
* <p>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.</p>
|
||||
* <p/>
|
||||
* <p>Example usage:</p>
|
||||
* <p/>
|
||||
* <pre>
|
||||
* 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());
|
||||
* </pre>
|
||||
* <p/>
|
||||
* <p>This class Requires API level 12 or later due to use of {@link
|
||||
* ViewPropertyAnimator}.</p>
|
||||
*/
|
||||
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<PendingDismissData> 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.</p>
|
||||
*
|
||||
* @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<PendingDismissData> {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
After Width: | Height: | Size: 696 B |
After Width: | Height: | Size: 621 B |
After Width: | Height: | Size: 505 B |
After Width: | Height: | Size: 482 B |
After Width: | Height: | Size: 860 B |
After Width: | Height: | Size: 780 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 673 B |
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Twidere - Twitter client for Android
|
||||
~
|
||||
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ This program is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tool="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<org.mariotaku.twidere.view.ShapedImageView
|
||||
android:id="@android:id/icon"
|
||||
style="?profileImageStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:textAppearance="?android:textAppearanceSmall"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textStyle="bold"
|
||||
tool:text="Name" />
|
||||
|
||||
</LinearLayout>
|
|
@ -51,6 +51,7 @@
|
|||
android:title="@string/enable_retweets" />
|
||||
<item
|
||||
android:id="@id/mute_user"
|
||||
android:checkable="true"
|
||||
android:icon="@drawable/ic_action_mic_muted"
|
||||
android:title="@string/twitter_mute_user" />
|
||||
<item
|
||||
|
|
|
@ -279,6 +279,10 @@
|
|||
<string name="activity_about_me_favorite_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> favorited your tweet.</string>
|
||||
<string name="activity_about_me_favorited_retweet"><xliff:g id="user">%s</xliff:g> favorited your retweet.</string>
|
||||
<string name="activity_about_me_favorited_retweet_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> favorited your retweet.</string>
|
||||
<string name="activity_about_me_like"><xliff:g id="user">%s</xliff:g> liked your tweet.</string>
|
||||
<string name="activity_about_me_like_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> liked your tweet.</string>
|
||||
<string name="activity_about_me_liked_retweet"><xliff:g id="user">%s</xliff:g> liked your retweet.</string>
|
||||
<string name="activity_about_me_liked_retweet_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> liked your retweet.</string>
|
||||
<string name="activity_about_me_follow"><xliff:g id="user">%s</xliff:g> is following you.</string>
|
||||
<string name="activity_about_me_follow_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> are following you.</string>
|
||||
<string name="activity_about_me_retweet"><xliff:g id="user">%s</xliff:g> retweeted your tweet.</string>
|
||||
|
@ -296,6 +300,8 @@
|
|||
<string name="activity_about_me_list_member_added_multi"><xliff:g id="user">%1$s</xliff:g> and <xliff:g id="other">%2$s</xliff:g> added you to their lists.</string>
|
||||
<string name="activity_by_friends_favorite"><xliff:g id="user">%1$s</xliff:g> favorited <xliff:g id="target">%2$s</xliff:g>\'s tweet.</string>
|
||||
<string name="activity_by_friends_favorite_multi"><xliff:g id="user">%1$s</xliff:g> favorited <xliff:g id="target">%2$s</xliff:g> and <xliff:g id="other">%3$s</xliff:g>\'s tweet.</string>
|
||||
<string name="activity_by_friends_like"><xliff:g id="user">%1$s</xliff:g> liked <xliff:g id="target">%2$s</xliff:g>\'s tweet.</string>
|
||||
<string name="activity_by_friends_like_multi"><xliff:g id="user">%1$s</xliff:g> liked <xliff:g id="target">%2$s</xliff:g> and <xliff:g id="other">%3$s</xliff:g>\'s tweet.</string>
|
||||
<string name="activity_by_friends_follow"><xliff:g id="user">%1$s</xliff:g> is following <xliff:g id="target">%2$s</xliff:g>.</string>
|
||||
<string name="activity_by_friends_follow_multi"><xliff:g id="user">%1$s</xliff:g> is following <xliff:g id="target">%2$s</xliff:g> and <xliff:g id="other">%3$s</xliff:g>.</string>
|
||||
<string name="activity_by_friends_retweet"><xliff:g id="user">%1$s</xliff:g> retweeted <xliff:g id="target">%2$s</xliff:g>\'s tweet.</string>
|
||||
|
@ -811,4 +817,6 @@
|
|||
<string name="twitter_optimized_searches_summary">Use special search terms to improve search results like exclude retweets</string>
|
||||
<string name="i_want_my_stars_back">I want my stars back!</string>
|
||||
<string name="i_want_my_stars_back_summary">Show use favorite (star) instead of like (heart)</string>
|
||||
<string name="copy_link">Copy link</string>
|
||||
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" fill="#ffffff"/>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
</svg>
|
After Width: | Height: | Size: 295 B |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.4.1 (15681) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Artboard</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="Artboard" sketch:type="MSArtboardGroup" fill="#FFFFFF">
|
||||
<path d="M12,20.4325 L10.695,19.2445 C6.06,15.0415 3,12.2695 3,8.8675 C3,6.0955 5.178,3.9175 7.95,3.9175 C9.516,3.9175 11.019,4.6465 12,5.7985 C12.981,4.6465 14.484,3.9175 16.05,3.9175 C18.822,3.9175 21,6.0955 21,8.8675 C21,12.2695 17.94,15.0415 13.305,19.2535 L12,20.4325 L12,20.4325 Z" id="Shape" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 972 B |