improved auto complete
|
@ -1,5 +1,4 @@
|
||||||
apply plugin: 'com.github.ben-manes.versions'
|
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.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -15,7 +14,6 @@ buildscript {
|
||||||
// should be excluded to avoid conflict
|
// should be excluded to avoid conflict
|
||||||
exclude group: 'xerces'
|
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
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// 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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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.soundcloud.android:android-crop:1.0.1@aar'
|
||||||
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
|
compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.1'
|
||||||
compile 'com.github.mariotaku:PickNCrop:1dff3ed574'
|
compile 'com.github.mariotaku:PickNCrop:1dff3ed574'
|
||||||
|
compile 'com.github.mariotaku:RestFu:0.9.2'
|
||||||
compile 'com.diogobernardino:williamchart:2.0.1'
|
compile 'com.diogobernardino:williamchart:2.0.1'
|
||||||
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
compile 'com.lnikkila:extendedtouchview:0.1.0'
|
||||||
compile 'com.google.dagger:dagger:2.0.1'
|
compile 'com.google.dagger:dagger:2.0.1'
|
||||||
compile 'org.attoparser:attoparser:1.4.0.RELEASE'
|
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'
|
googleCompile 'com.google.maps.android:android-maps-utils:0.4'
|
||||||
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
fdroidCompile 'org.osmdroid:osmdroid-android:4.3'
|
||||||
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
fdroidCompile 'org.slf4j:slf4j-simple:1.7.12'
|
||||||
|
|
|
@ -341,6 +341,10 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".activity.CopyLinkActivity"
|
||||||
|
android:label="@string/copy_link"
|
||||||
|
android:theme="@style/Theme.Twidere.Dark.NoDisplay" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.support.RequestPermissionsActivity"
|
android:name=".activity.support.RequestPermissionsActivity"
|
||||||
android:label="@string/permissions_request"
|
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;
|
package org.mariotaku.twidere.activity.support;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
@ -44,38 +45,42 @@ import android.view.WindowManager;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.mobeta.android.dslv.DragSortCursorAdapter;
|
|
||||||
|
|
||||||
import org.mariotaku.twidere.R;
|
import org.mariotaku.twidere.R;
|
||||||
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
|
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
|
||||||
import org.mariotaku.twidere.model.ParcelableAccount;
|
import org.mariotaku.twidere.model.ParcelableAccount;
|
||||||
import org.mariotaku.twidere.model.ParcelableCredentials;
|
import org.mariotaku.twidere.model.ParcelableCredentials;
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.SearchHistory;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
|
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
|
||||||
import org.mariotaku.twidere.util.EditTextEnterHandler;
|
import org.mariotaku.twidere.util.EditTextEnterHandler;
|
||||||
import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener;
|
import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener;
|
||||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||||
import org.mariotaku.twidere.util.ParseUtils;
|
import org.mariotaku.twidere.util.ParseUtils;
|
||||||
|
import org.mariotaku.twidere.util.SwipeDismissListViewTouchListener;
|
||||||
import org.mariotaku.twidere.util.ThemeUtils;
|
import org.mariotaku.twidere.util.ThemeUtils;
|
||||||
import org.mariotaku.twidere.util.UserColorNameManager;
|
import org.mariotaku.twidere.util.UserColorNameManager;
|
||||||
import org.mariotaku.twidere.util.Utils;
|
import org.mariotaku.twidere.util.Utils;
|
||||||
|
import org.mariotaku.twidere.util.content.ContentResolverUtils;
|
||||||
import org.mariotaku.twidere.view.ExtendedRelativeLayout;
|
import org.mariotaku.twidere.view.ExtendedRelativeLayout;
|
||||||
import org.mariotaku.twidere.view.iface.IExtendedView.OnFitSystemWindowsListener;
|
import org.mariotaku.twidere.view.iface.IExtendedView.OnFitSystemWindowsListener;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jopt.csp.util.SortableIntList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/1/6.
|
* Created by mariotaku on 15/1/6.
|
||||||
*/
|
*/
|
||||||
public class QuickSearchBarActivity extends ThemedFragmentActivity implements OnClickListener,
|
public class QuickSearchBarActivity extends ThemedFragmentActivity implements OnClickListener,
|
||||||
LoaderCallbacks<Cursor>, OnItemSelectedListener, OnItemClickListener,
|
LoaderCallbacks<Cursor>, OnItemSelectedListener, OnItemClickListener,
|
||||||
OnFitSystemWindowsListener {
|
OnFitSystemWindowsListener, SwipeDismissListViewTouchListener.DismissCallbacks {
|
||||||
|
|
||||||
private Spinner mAccountSpinner;
|
private Spinner mAccountSpinner;
|
||||||
private EditText mSearchQuery;
|
private EditText mSearchQuery;
|
||||||
|
@ -86,19 +91,24 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
private Rect mSystemWindowsInsets = new Rect();
|
private Rect mSystemWindowsInsets = new Rect();
|
||||||
private boolean mTextChanged;
|
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) {
|
public void onDismiss(ListView listView, int[] reverseSortedPositions) {
|
||||||
// final long[] ids = new long[reverseSortedPositions.length];
|
final Long[] ids = new Long[reverseSortedPositions.length];
|
||||||
// for (int i = 0, j = reverseSortedPositions.length; i < j; i++) {
|
for (int i = 0, j = reverseSortedPositions.length; i < j; i++) {
|
||||||
// final int position = reverseSortedPositions[i];
|
final int position = reverseSortedPositions[i];
|
||||||
// final SearchHistoryItem item = (SearchHistoryItem) mUsersSearchAdapter.getItem(position);
|
final SuggestionItem item = mUsersSearchAdapter.getSuggestionItem(position);
|
||||||
// mUsersSearchAdapter.removeItemAt(position);
|
ids[i] = item._id;
|
||||||
// ids[i] = item.getCursorId();
|
}
|
||||||
// }
|
mUsersSearchAdapter.addRemovedPositions(reverseSortedPositions);
|
||||||
// final ContentResolver cr = getContentResolver();
|
final ContentResolver cr = getContentResolver();
|
||||||
// final Long[] idsObject = ArrayUtils.toObject(ids);
|
ContentResolverUtils.bulkDelete(cr, SearchHistory.CONTENT_URI, SearchHistory._ID, ids,
|
||||||
// ContentResolverUtils.bulkDelete(cr, SearchHistory.CONTENT_URI, SearchHistory._ID, idsObject,
|
null, false);
|
||||||
// null, false);
|
getSupportLoaderManager().restartLoader(0, null, this);
|
||||||
// getSupportLoaderManager().restartLoader(0, null, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -224,6 +234,10 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
mUsersSearchAdapter = new SuggestionsAdapter(this);
|
mUsersSearchAdapter = new SuggestionsAdapter(this);
|
||||||
mSuggestionsList.setAdapter(mUsersSearchAdapter);
|
mSuggestionsList.setAdapter(mUsersSearchAdapter);
|
||||||
mSuggestionsList.setOnItemClickListener(this);
|
mSuggestionsList.setOnItemClickListener(this);
|
||||||
|
|
||||||
|
final SwipeDismissListViewTouchListener listener = new SwipeDismissListViewTouchListener(mSuggestionsList, this);
|
||||||
|
mSuggestionsList.setOnTouchListener(listener);
|
||||||
|
mSuggestionsList.setOnScrollListener(listener.makeScrollListener());
|
||||||
mSearchSubmit.setOnClickListener(this);
|
mSearchSubmit.setOnClickListener(this);
|
||||||
|
|
||||||
EditTextEnterHandler.attach(mSearchQuery, new EnterListener() {
|
EditTextEnterHandler.attach(mSearchQuery, new EnterListener() {
|
||||||
|
@ -296,16 +310,17 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
|
|
||||||
|
|
||||||
public final String title, summary;
|
public final String title, summary;
|
||||||
private final long extra_id;
|
public final long _id, extra_id;
|
||||||
|
|
||||||
public SuggestionItem(Cursor cursor, SuggestionsAdapter.Indices indices) {
|
public SuggestionItem(Cursor cursor, SuggestionsAdapter.Indices indices) {
|
||||||
|
_id = cursor.getLong(indices._id);
|
||||||
title = cursor.getString(indices.title);
|
title = cursor.getString(indices.title);
|
||||||
summary = cursor.getString(indices.summary);
|
summary = cursor.getString(indices.summary);
|
||||||
extra_id = cursor.getLong(indices.extra_id);
|
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_SEARCH_HISTORY = 0;
|
||||||
static final int VIEW_TYPE_SAVED_SEARCH = 1;
|
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 MediaLoaderWrapper mImageLoader;
|
||||||
private final UserColorNameManager mUserColorNameManager;
|
private final UserColorNameManager mUserColorNameManager;
|
||||||
private final QuickSearchBarActivity mActivity;
|
private final QuickSearchBarActivity mActivity;
|
||||||
|
private final SortableIntList mRemovedPositions;
|
||||||
private Indices mIndices;
|
private Indices mIndices;
|
||||||
|
|
||||||
SuggestionsAdapter(QuickSearchBarActivity activity) {
|
SuggestionsAdapter(QuickSearchBarActivity activity) {
|
||||||
super(activity, null, 0);
|
super(activity, null, 0);
|
||||||
|
mRemovedPositions = new SortableIntList();
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
mImageLoader = activity.mImageLoader;
|
mImageLoader = activity.mImageLoader;
|
||||||
mUserColorNameManager = activity.mUserColorNameManager;
|
mUserColorNameManager = activity.mUserColorNameManager;
|
||||||
mInflater = LayoutInflater.from(activity);
|
mInflater = LayoutInflater.from(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
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_SEARCH_HISTORY:
|
||||||
case VIEW_TYPE_SAVED_SEARCH: {
|
case VIEW_TYPE_SAVED_SEARCH: {
|
||||||
final View view = mInflater.inflate(R.layout.list_item_suggestion_search, parent, false);
|
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) {
|
public SuggestionItem getSuggestionItem(int position) {
|
||||||
final Cursor cursor = (Cursor) super.getItem(position);
|
final Cursor cursor = (Cursor) getItem(position);
|
||||||
return new SuggestionItem(cursor, mIndices);
|
return new SuggestionItem(cursor, mIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
switch (getItemViewType(cursor.getPosition())) {
|
switch (getActualItemViewType(cursor.getPosition())) {
|
||||||
case VIEW_TYPE_SEARCH_HISTORY: {
|
case VIEW_TYPE_SEARCH_HISTORY: {
|
||||||
final SearchViewHolder holder = (SearchViewHolder) view.getTag();
|
final SearchViewHolder holder = (SearchViewHolder) view.getTag();
|
||||||
final String title = cursor.getString(mIndices.title);
|
final String title = cursor.getString(mIndices.title);
|
||||||
|
@ -400,7 +412,11 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
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)) {
|
switch (cursor.getString(mIndices.type)) {
|
||||||
case Suggestions.Search.TYPE_SAVED_SEARCH: {
|
case Suggestions.Search.TYPE_SAVED_SEARCH: {
|
||||||
return VIEW_TYPE_SAVED_SEARCH;
|
return VIEW_TYPE_SAVED_SEARCH;
|
||||||
|
@ -440,9 +456,55 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
} else {
|
} else {
|
||||||
mIndices = null;
|
mIndices = null;
|
||||||
}
|
}
|
||||||
|
mRemovedPositions.clear();
|
||||||
return super.swapCursor(newCursor);
|
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 {
|
static class SearchViewHolder {
|
||||||
|
|
||||||
private final ImageView icon;
|
private final ImageView icon;
|
||||||
|
@ -471,6 +533,7 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Indices {
|
private static class Indices {
|
||||||
|
private final int _id;
|
||||||
private final int type;
|
private final int type;
|
||||||
private final int title;
|
private final int title;
|
||||||
private final int summary;
|
private final int summary;
|
||||||
|
@ -478,6 +541,7 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
|
||||||
private final int extra_id;
|
private final int extra_id;
|
||||||
|
|
||||||
public Indices(Cursor cursor) {
|
public Indices(Cursor cursor) {
|
||||||
|
_id = cursor.getColumnIndex(Suggestions._ID);
|
||||||
type = cursor.getColumnIndex(Suggestions.TYPE);
|
type = cursor.getColumnIndex(Suggestions.TYPE);
|
||||||
title = cursor.getColumnIndex(Suggestions.TITLE);
|
title = cursor.getColumnIndex(Suggestions.TITLE);
|
||||||
summary = cursor.getColumnIndex(Suggestions.SUMMARY);
|
summary = cursor.getColumnIndex(Suggestions.SUMMARY);
|
||||||
|
|
|
@ -58,7 +58,8 @@ import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder;
|
||||||
* Created by mariotaku on 15/1/3.
|
* Created by mariotaku on 15/1/3.
|
||||||
*/
|
*/
|
||||||
public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<ViewHolder> implements Constants,
|
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_STUB = 0;
|
||||||
private static final int ITEM_VIEW_TYPE_GAP = 1;
|
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 mDisplayMediaPreview;
|
||||||
private final boolean mNameFirst;
|
private final boolean mNameFirst;
|
||||||
private final boolean mDisplayProfileImage;
|
private final boolean mDisplayProfileImage;
|
||||||
|
private final boolean mShouldUseStarsForLikes;
|
||||||
private final TwidereLinkify mLinkify;
|
private final TwidereLinkify mLinkify;
|
||||||
private final DummyStatusHolderAdapter mStatusAdapterDelegate;
|
private final DummyStatusHolderAdapter mStatusAdapterDelegate;
|
||||||
private ActivityAdapterListener mActivityAdapterListener;
|
private ActivityAdapterListener mActivityAdapterListener;
|
||||||
|
@ -87,6 +89,7 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
|
||||||
mInflater = LayoutInflater.from(context);
|
mInflater = LayoutInflater.from(context);
|
||||||
mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress);
|
mLoadingHandler = new MediaLoadingHandler(R.id.media_preview_progress);
|
||||||
mTextSize = mPreferences.getInt(KEY_TEXT_SIZE, context.getResources().getInteger(R.integer.default_text_size));
|
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;
|
mCompactCards = compact;
|
||||||
mProfileImageStyle = Utils.getProfileImageStyle(mPreferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
|
mProfileImageStyle = Utils.getProfileImageStyle(mPreferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
|
||||||
mMediaPreviewStyle = Utils.getMediaPreviewStyle(mPreferences.getString(KEY_MEDIA_PREVIEW_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
|
@Override
|
||||||
public void onUserProfileClick(IStatusViewHolder holder, int position) {
|
public void onUserProfileClick(IStatusViewHolder holder, int position) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
|
|
@ -19,10 +19,7 @@
|
||||||
|
|
||||||
package org.mariotaku.twidere.adapter.iface;
|
package org.mariotaku.twidere.adapter.iface;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.mariotaku.twidere.model.ParcelableActivity;
|
import org.mariotaku.twidere.model.ParcelableActivity;
|
||||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
|
||||||
import org.mariotaku.twidere.util.MediaLoadingHandler;
|
import org.mariotaku.twidere.util.MediaLoadingHandler;
|
||||||
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
|
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
|
||||||
|
|
||||||
|
@ -40,9 +37,7 @@ public interface IActivitiesAdapter<Data> extends IContentCardAdapter, IGapSuppo
|
||||||
@PreviewStyle
|
@PreviewStyle
|
||||||
int getMediaPreviewStyle();
|
int getMediaPreviewStyle();
|
||||||
|
|
||||||
@NonNull
|
boolean shouldUseStarsForLikes();
|
||||||
@Override
|
|
||||||
MediaLoaderWrapper getMediaLoader();
|
|
||||||
|
|
||||||
MediaLoadingHandler getMediaLoadingHandler();
|
MediaLoadingHandler getMediaLoadingHandler();
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.mariotaku.twidere.model.ParcelableStatus;
|
||||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
|
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
|
||||||
|
import org.mariotaku.twidere.util.LinkCreator;
|
||||||
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
|
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
|
||||||
import org.mariotaku.twidere.util.RecyclerViewUtils;
|
import org.mariotaku.twidere.util.RecyclerViewUtils;
|
||||||
import org.mariotaku.twidere.util.Utils;
|
import org.mariotaku.twidere.util.Utils;
|
||||||
|
@ -121,7 +122,9 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListRecyclerVi
|
||||||
if (status == null) return false;
|
if (status == null) return false;
|
||||||
if (item.getItemId() == R.id.share) {
|
if (item.getItemId() == R.id.share) {
|
||||||
final Intent shareIntent = Utils.createStatusShareIntent(getActivity(), status);
|
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 true;
|
||||||
}
|
}
|
||||||
return Utils.handleMenuItemClick(getActivity(), AbsStatusesFragment.this,
|
return Utils.handleMenuItemClick(getActivity(), AbsStatusesFragment.this,
|
||||||
|
|
|
@ -203,7 +203,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
|
||||||
} else {
|
} else {
|
||||||
positiveButton.setText(isMyRetweet(status) ? R.string.cancel_retweet : R.string.retweet);
|
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);
|
final StatusTextCountView textCountView = (StatusTextCountView) alertDialog.findViewById(R.id.comment_text_count);
|
||||||
textCountView.setTextCount(mValidator.getTweetLength(s + " " + statusLink));
|
textCountView.setTextCount(mValidator.getTweetLength(s + " " + statusLink));
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,7 +288,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||||
final ParcelableStatus status = getStatus();
|
final ParcelableStatus status = getStatus();
|
||||||
if (status == null) return null;
|
if (status == null) return null;
|
||||||
return new NdefMessage(new NdefRecord[]{
|
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.internal.widget.ActionBarContainer;
|
||||||
import android.support.v7.widget.ActionMenuView;
|
import android.support.v7.widget.ActionMenuView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.Html;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
@ -869,9 +868,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
|
||||||
}
|
}
|
||||||
final MenuItem muteItem = menu.findItem(R.id.mute_user);
|
final MenuItem muteItem = menu.findItem(R.id.mute_user);
|
||||||
if (muteItem != null) {
|
if (muteItem != null) {
|
||||||
final boolean muting = relationship.isSourceMutingTarget();
|
muteItem.setChecked(relationship.isSourceMutingTarget());
|
||||||
ActionIconDrawable.setMenuHighlight(muteItem, new TwidereMenuInfo(muting));
|
|
||||||
muteItem.setTitle(muting ? R.string.unmute : R.string.mute);
|
|
||||||
}
|
}
|
||||||
final MenuItem filterItem = menu.findItem(R.id.add_to_filter);
|
final MenuItem filterItem = menu.findItem(R.id.add_to_filter);
|
||||||
if (filterItem != null) {
|
if (filterItem != null) {
|
||||||
|
@ -881,7 +878,6 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener
|
||||||
}
|
}
|
||||||
final MenuItem wantRetweetsItem = menu.findItem(R.id.enable_retweets);
|
final MenuItem wantRetweetsItem = menu.findItem(R.id.enable_retweets);
|
||||||
if (wantRetweetsItem != null) {
|
if (wantRetweetsItem != null) {
|
||||||
|
|
||||||
wantRetweetsItem.setChecked(relationship.isSourceWantRetweetsFromTarget());
|
wantRetweetsItem.setChecked(relationship.isSourceWantRetweetsFromTarget());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -400,7 +400,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList
|
||||||
|
|
||||||
private void setupUserPages() {
|
private void setupUserPages() {
|
||||||
final Bundle args = getArguments(), tabArgs = new Bundle();
|
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);
|
final ParcelableUserList userList = args.getParcelable(EXTRA_USER_LIST);
|
||||||
tabArgs.putLong(EXTRA_ACCOUNT_ID, userList.account_id);
|
tabArgs.putLong(EXTRA_ACCOUNT_ID, userList.account_id);
|
||||||
tabArgs.putLong(EXTRA_USER_ID, userList.user_id);
|
tabArgs.putLong(EXTRA_USER_ID, userList.user_id);
|
||||||
|
|
|
@ -26,17 +26,15 @@ import android.content.IntentFilter;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
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.Twitter;
|
||||||
import org.mariotaku.twidere.api.twitter.TwitterException;
|
import org.mariotaku.twidere.api.twitter.TwitterException;
|
||||||
import org.mariotaku.twidere.api.twitter.model.UserList;
|
import org.mariotaku.twidere.api.twitter.model.UserList;
|
||||||
|
import org.mariotaku.twidere.loader.support.CursorSupportUsersLoader;
|
||||||
import static org.mariotaku.twidere.util.TwitterAPIFactory.getTwitterInstance;
|
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 {
|
public class UserListMembersFragment extends CursorSupportUsersListFragment {
|
||||||
|
|
||||||
|
@ -107,7 +105,7 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment {
|
||||||
super.onStop();
|
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 accountId, userId;
|
||||||
private final long listId;
|
private final long listId;
|
||||||
|
@ -123,7 +121,7 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ParcelableUserList doInBackground(final Object... params) {
|
protected SingleResponse<ParcelableUserList> doInBackground(final Object... params) {
|
||||||
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(getActivity(), accountId, true);
|
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(getActivity(), accountId, true);
|
||||||
if (twitter == null) return null;
|
if (twitter == null) return null;
|
||||||
try {
|
try {
|
||||||
|
@ -136,17 +134,16 @@ public class UserListMembersFragment extends CursorSupportUsersListFragment {
|
||||||
list = twitter.showUserList(listName, screenName);
|
list = twitter.showUserList(listName, screenName);
|
||||||
} else
|
} else
|
||||||
return null;
|
return null;
|
||||||
return new ParcelableUserList(list, accountId);
|
return SingleResponse.getInstance(new ParcelableUserList(list, accountId));
|
||||||
} catch (final TwitterException e) {
|
} catch (final TwitterException e) {
|
||||||
e.printStackTrace();
|
return SingleResponse.getInstance(e);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(final ParcelableUserList result) {
|
protected void onPostExecute(final SingleResponse<ParcelableUserList> result) {
|
||||||
if (mUserList != null) return;
|
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 String query = uri.getQueryParameter(QUERY_PARAM_QUERY);
|
||||||
final long accountId = ParseUtils.parseLong(uri.getQueryParameter(QUERY_PARAM_ACCOUNT_ID), -1);
|
final long accountId = ParseUtils.parseLong(uri.getQueryParameter(QUERY_PARAM_ACCOUNT_ID), -1);
|
||||||
if (query == null || accountId <= 0) return null;
|
if (query == null || accountId <= 0) return null;
|
||||||
final ContentResolver resolver = getContentResolver();
|
|
||||||
final boolean emptyQuery = TextUtils.isEmpty(query);
|
final boolean emptyQuery = TextUtils.isEmpty(query);
|
||||||
final String queryEscaped = query.replace("_", "^_");
|
final String queryEscaped = query.replace("_", "^_");
|
||||||
final Cursor[] cursors;
|
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(CachedUsers.USER_ID, Suggestions.Search.EXTRA_ID).getSQL(),
|
||||||
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).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 long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager);
|
||||||
final Expression usersSelection = Expression.or(
|
final Expression usersSelection = Expression.or(
|
||||||
Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
|
Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
|
||||||
Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"),
|
Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"),
|
||||||
Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
|
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 String[] order = {CachedUsers.LAST_SEEN, "score", CachedUsers.SCREEN_NAME, CachedUsers.NAME};
|
||||||
final boolean[] ascending = {false, false, true, true};
|
final boolean[] ascending = {false, false, true, true};
|
||||||
final OrderBy orderBy = new OrderBy(order, ascending);
|
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 Expression exactUserSelection = Expression.or(Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?", "^"));
|
||||||
final Cursor exactUserCursor = mDatabaseWrapper.query(CachedUsers.TABLE_NAME,
|
final Cursor exactUserCursor = mDatabaseWrapper.query(CachedUsers.TABLE_NAME,
|
||||||
new String[]{SQLFunctions.COUNT()}, exactUserSelection.getSQL(),
|
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;
|
final boolean hasName = exactUserCursor.moveToPosition(0) && exactUserCursor.getInt(0) > 0;
|
||||||
exactUserCursor.close();
|
exactUserCursor.close();
|
||||||
final MatrixCursor screenNameCursor = new MatrixCursor(Suggestions.Search.COLUMNS);
|
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.USER_ID, Suggestions.EXTRA_ID).getSQL(),
|
||||||
new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.ICON).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),
|
return query(Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountId),
|
||||||
mappedProjection, where.getSQL(), whereArgs, new OrderBy(new String[]{"score", CachedUsers.LAST_SEEN},
|
mappedProjection, where.getSQL(), whereArgs, new OrderBy(orderBy, ascending).getSQL());
|
||||||
new boolean[]{false, false}).getSQL());
|
|
||||||
} else if (Suggestions.AutoComplete.TYPE_HASHTAGS.equals(type)) {
|
} else if (Suggestions.AutoComplete.TYPE_HASHTAGS.equals(type)) {
|
||||||
final Expression where = Expression.likeRaw(new Column(CachedHashtags.NAME), "?||'%'", "^");
|
final Expression where = Expression.likeRaw(new Column(CachedHashtags.NAME), "?||'%'", "^");
|
||||||
final String[] whereArgs = new String[]{queryEscaped};
|
final String[] whereArgs = new String[]{queryEscaped};
|
||||||
|
|
|
@ -878,22 +878,18 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true);
|
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true);
|
||||||
if (twitter == null) return SingleResponse.getInstance();
|
if (twitter == null) return SingleResponse.getInstance();
|
||||||
try {
|
try {
|
||||||
final org.mariotaku.twidere.api.twitter.model.Status status = twitter.createFavorite(status_id);
|
final ParcelableStatus status = new ParcelableStatus(twitter.createFavorite(status_id), account_id, false);
|
||||||
Utils.setLastSeen(mContext, status.getUserMentionEntities(), System.currentTimeMillis());
|
Utils.setLastSeen(mContext, status.mentions, System.currentTimeMillis());
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
values.put(Statuses.IS_FAVORITE, true);
|
values.put(Statuses.IS_FAVORITE, true);
|
||||||
if (status.isRetweet()) {
|
values.put(Statuses.FAVORITE_COUNT, status.favorite_count);
|
||||||
values.put(Statuses.FAVORITE_COUNT, status.getRetweetedStatus().getFavoriteCount());
|
|
||||||
} else {
|
|
||||||
values.put(Statuses.FAVORITE_COUNT, status.getFavoriteCount());
|
|
||||||
}
|
|
||||||
final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id),
|
final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id),
|
||||||
Expression.or(Expression.equals(Statuses.STATUS_ID, status_id),
|
Expression.or(Expression.equals(Statuses.STATUS_ID, status_id),
|
||||||
Expression.equals(Statuses.RETWEET_ID, status_id)));
|
Expression.equals(Statuses.RETWEET_ID, status_id)));
|
||||||
for (final Uri uri : TwidereDataStore.STATUSES_URIS) {
|
for (final Uri uri : TwidereDataStore.STATUSES_URIS) {
|
||||||
mResolver.update(uri, values, where.getSQL(), null);
|
mResolver.update(uri, values, where.getSQL(), null);
|
||||||
}
|
}
|
||||||
return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false));
|
return SingleResponse.getInstance(status);
|
||||||
} catch (final TwitterException e) {
|
} catch (final TwitterException e) {
|
||||||
Log.w(LOGTAG, e);
|
Log.w(LOGTAG, e);
|
||||||
return SingleResponse.getInstance(e);
|
return SingleResponse.getInstance(e);
|
||||||
|
@ -1516,20 +1512,17 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true);
|
final Twitter twitter = TwitterAPIFactory.getTwitterInstance(mContext, account_id, true);
|
||||||
if (twitter != null) {
|
if (twitter != null) {
|
||||||
try {
|
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();
|
final ContentValues values = new ContentValues();
|
||||||
values.put(Statuses.IS_FAVORITE, false);
|
values.put(Statuses.IS_FAVORITE, false);
|
||||||
if (status.isRetweet()) {
|
values.put(Statuses.FAVORITE_COUNT, status.favorite_count - 1);
|
||||||
values.put(Statuses.FAVORITE_COUNT, status.getRetweetedStatus().getFavoriteCount());
|
|
||||||
} else {
|
|
||||||
values.put(Statuses.FAVORITE_COUNT, status.getFavoriteCount());
|
|
||||||
}
|
|
||||||
final Expression where = Expression.and(Expression.equals(Statuses.ACCOUNT_ID, account_id),
|
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) {
|
for (final Uri uri : TwidereDataStore.STATUSES_URIS) {
|
||||||
mResolver.update(uri, values, where.getSQL(), null);
|
mResolver.update(uri, values, where.getSQL(), null);
|
||||||
}
|
}
|
||||||
return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false));
|
return SingleResponse.getInstance(status);
|
||||||
} catch (final TwitterException e) {
|
} catch (final TwitterException e) {
|
||||||
return SingleResponse.getInstance(e);
|
return SingleResponse.getInstance(e);
|
||||||
}
|
}
|
||||||
|
@ -1730,6 +1723,9 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
if (status != null || exception.getErrorCode() == HttpResponseCode.NOT_FOUND) {
|
if (status != null || exception.getErrorCode() == HttpResponseCode.NOT_FOUND) {
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
values.put(Statuses.MY_RETWEET_ID, -1);
|
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) {
|
for (final Uri uri : TwidereDataStore.STATUSES_URIS) {
|
||||||
mResolver.delete(uri, Statuses.STATUS_ID + " = " + status_id, null);
|
mResolver.delete(uri, Statuses.STATUS_ID + " = " + status_id, null);
|
||||||
mResolver.update(uri, values, Statuses.MY_RETWEET_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();
|
return SingleResponse.getInstance();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final org.mariotaku.twidere.api.twitter.model.Status status = twitter.retweetStatus(status_id);
|
final ParcelableStatus status = new ParcelableStatus(twitter.retweetStatus(status_id), account_id, false);
|
||||||
Utils.setLastSeen(mContext, status.getUserMentionEntities(), System.currentTimeMillis());
|
Utils.setLastSeen(mContext, status.mentions, System.currentTimeMillis());
|
||||||
return SingleResponse.getInstance(new ParcelableStatus(status, account_id, false));
|
return SingleResponse.getInstance(status);
|
||||||
} catch (final TwitterException e) {
|
} catch (final TwitterException e) {
|
||||||
return SingleResponse.getInstance(e);
|
return SingleResponse.getInstance(e);
|
||||||
}
|
}
|
||||||
|
@ -2606,9 +2602,10 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
protected void onPostExecute(final SingleResponse<ParcelableStatus> result) {
|
protected void onPostExecute(final SingleResponse<ParcelableStatus> result) {
|
||||||
mCreatingRetweetIds.remove(account_id, status_id);
|
mCreatingRetweetIds.remove(account_id, status_id);
|
||||||
if (result.hasData()) {
|
if (result.hasData()) {
|
||||||
final ContentValues values = new ContentValues();
|
|
||||||
final ParcelableStatus status = result.getData();
|
final ParcelableStatus status = result.getData();
|
||||||
|
final ContentValues values = new ContentValues();
|
||||||
values.put(Statuses.MY_RETWEET_ID, status.id);
|
values.put(Statuses.MY_RETWEET_ID, status.id);
|
||||||
|
values.put(Statuses.RETWEET_COUNT, status.retweet_count);
|
||||||
final Expression where = Expression.or(
|
final Expression where = Expression.or(
|
||||||
Expression.equals(Statuses.STATUS_ID, status_id),
|
Expression.equals(Statuses.STATUS_ID, status_id),
|
||||||
Expression.equals(Statuses.RETWEET_ID, status_id)
|
Expression.equals(Statuses.RETWEET_ID, status_id)
|
||||||
|
@ -2625,7 +2622,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
|
||||||
|
|
||||||
// END HotMobi
|
// END HotMobi
|
||||||
|
|
||||||
|
|
||||||
bus.post(new StatusRetweetedEvent(status));
|
bus.post(new StatusRetweetedEvent(status));
|
||||||
Utils.showOkMessage(mContext, R.string.status_retweeted, false);
|
Utils.showOkMessage(mContext, R.string.status_retweeted, false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,6 +22,7 @@ package org.mariotaku.twidere.util;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import org.mariotaku.twidere.Constants;
|
import org.mariotaku.twidere.Constants;
|
||||||
|
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 15/3/14.
|
* Created by mariotaku on 15/3/14.
|
||||||
|
@ -83,4 +84,8 @@ public class LinkCreator implements Constants {
|
||||||
builder.appendPath(screenName);
|
builder.appendPath(screenName);
|
||||||
return builder.build();
|
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.BuildConfig;
|
||||||
import org.mariotaku.twidere.Constants;
|
import org.mariotaku.twidere.Constants;
|
||||||
import org.mariotaku.twidere.R;
|
import org.mariotaku.twidere.R;
|
||||||
|
import org.mariotaku.twidere.activity.CopyLinkActivity;
|
||||||
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
|
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
|
||||||
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
|
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
|
||||||
import org.mariotaku.twidere.activity.support.MediaViewerActivity;
|
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.ParcelableStatus;
|
||||||
import org.mariotaku.twidere.model.ParcelableUser;
|
import org.mariotaku.twidere.model.ParcelableUser;
|
||||||
import org.mariotaku.twidere.model.ParcelableUserList;
|
import org.mariotaku.twidere.model.ParcelableUserList;
|
||||||
|
import org.mariotaku.twidere.model.ParcelableUserMention;
|
||||||
import org.mariotaku.twidere.model.PebbleMessage;
|
import org.mariotaku.twidere.model.PebbleMessage;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore;
|
import org.mariotaku.twidere.provider.TwidereDataStore;
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
|
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) {
|
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,
|
return context.getString(R.string.status_share_text_format_with_link,
|
||||||
status.text_plain, link.toString());
|
status.text_plain, link.toString());
|
||||||
}
|
}
|
||||||
|
@ -1631,6 +1633,15 @@ public final class Utils implements Constants {
|
||||||
return textView;
|
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) {
|
public static boolean setLastSeen(Context context, UserMentionEntity[] entities, long time) {
|
||||||
if (entities == null) return false;
|
if (entities == null) return false;
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
|
@ -3379,11 +3390,20 @@ public final class Utils implements Constants {
|
||||||
addIntentToMenu(context, shareSubMenu, shareIntent, MENU_GROUP_STATUS_SHARE);
|
addIntentToMenu(context, shareSubMenu, shareIntent, MENU_GROUP_STATUS_SHARE);
|
||||||
} else {
|
} else {
|
||||||
final Intent shareIntent = createStatusShareIntent(context, status);
|
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) {
|
private static boolean isMyStatus(ParcelableStatus status) {
|
||||||
if (isMyRetweet(status)) return true;
|
if (isMyRetweet(status)) return true;
|
||||||
return status.account_id == status.user_id;
|
return status.account_id == status.user_id;
|
||||||
|
|
|
@ -102,14 +102,28 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Activity.ACTION_FAVORITE: {
|
case Activity.ACTION_FAVORITE: {
|
||||||
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
if (adapter.shouldUseStarsForLikes()) {
|
||||||
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP);
|
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
||||||
if (byFriends) {
|
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP);
|
||||||
titleView.setText(getTitleStringByFriends(R.string.activity_by_friends_favorite,
|
|
||||||
R.string.activity_by_friends_favorite_multi, activity.sources, activity.target_statuses));
|
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 {
|
} else {
|
||||||
titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorite,
|
activityTypeView.setImageResource(R.drawable.ic_activity_action_like);
|
||||||
R.string.activity_about_me_favorite_multi, activity.sources));
|
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);
|
displayUserProfileImages(activity.sources);
|
||||||
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
||||||
|
@ -135,10 +149,17 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O
|
||||||
showNotSupported();
|
showNotSupported();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
if (adapter.shouldUseStarsForLikes()) {
|
||||||
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP);
|
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
||||||
titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_favorited_retweet,
|
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP);
|
||||||
R.string.activity_about_me_favorited_retweet_multi, activity.sources));
|
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);
|
displayUserProfileImages(activity.sources);
|
||||||
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
||||||
summaryView.setVisibility(View.VISIBLE);
|
summaryView.setVisibility(View.VISIBLE);
|
||||||
|
@ -177,10 +198,17 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O
|
||||||
showNotSupported();
|
showNotSupported();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
if (adapter.shouldUseStarsForLikes()) {
|
||||||
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_like), Mode.SRC_ATOP);
|
activityTypeView.setImageResource(R.drawable.ic_activity_action_favorite);
|
||||||
titleView.setText(getTitleStringAboutMe(R.string.activity_about_me_liked_mention,
|
activityTypeView.setColorFilter(ContextCompat.getColor(context, R.color.highlight_favorite), Mode.SRC_ATOP);
|
||||||
R.string.activity_about_me_liked_mention_multi, activity.sources));
|
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);
|
displayUserProfileImages(activity.sources);
|
||||||
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
summaryView.setText(activity.target_statuses[0].text_unescaped);
|
||||||
summaryView.setVisibility(View.VISIBLE);
|
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" />
|
android:title="@string/enable_retweets" />
|
||||||
<item
|
<item
|
||||||
android:id="@id/mute_user"
|
android:id="@id/mute_user"
|
||||||
|
android:checkable="true"
|
||||||
android:icon="@drawable/ic_action_mic_muted"
|
android:icon="@drawable/ic_action_mic_muted"
|
||||||
android:title="@string/twitter_mute_user" />
|
android:title="@string/twitter_mute_user" />
|
||||||
<item
|
<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_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"><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_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"><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_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>
|
<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_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"><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_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"><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_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>
|
<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="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">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="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>
|
</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 |