added drag sort and swipe remove for status media, improved situations mentioned in #249
This commit is contained in:
parent
95c976a741
commit
0859d66d8f
|
@ -7,7 +7,7 @@ android:
|
|||
- tools
|
||||
|
||||
# The BuildTools version used by your project
|
||||
- build-tools-23.0.1
|
||||
- build-tools-23.0.2
|
||||
|
||||
# The SDK version used to compile your project
|
||||
- android-23
|
||||
|
|
|
@ -32,7 +32,7 @@ subprojects {
|
|||
if (project.hasProperty('android')) {
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.1'
|
||||
buildToolsVersion '23.0.2'
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.mariotaku.twidere.model;
|
|||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
|
@ -28,6 +29,8 @@ public class ParcelableMediaUpdate implements Parcelable {
|
|||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@NonNull
|
||||
@JsonField(name = "uri")
|
||||
public String uri;
|
||||
@JsonField(name = "type")
|
||||
|
@ -41,7 +44,7 @@ public class ParcelableMediaUpdate implements Parcelable {
|
|||
type = in.readInt();
|
||||
}
|
||||
|
||||
public ParcelableMediaUpdate(final String uri, final int type) {
|
||||
public ParcelableMediaUpdate(@NonNull final String uri, final int type) {
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
}
|
||||
|
@ -62,6 +65,25 @@ public class ParcelableMediaUpdate implements Parcelable {
|
|||
dest.writeInt(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ParcelableMediaUpdate that = (ParcelableMediaUpdate) o;
|
||||
|
||||
if (type != that.type) return false;
|
||||
return uri.equals(that.uri);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = uri.hashCode();
|
||||
result = 31 * result + type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static ParcelableMediaUpdate[] fromJSONString(final String json) {
|
||||
if (TextUtils.isEmpty(json)) return null;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Jacek Marchwicki <jacek.marchwicki@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mariotaku.dynamicgridview;
|
||||
|
||||
public interface DraggableAdapter {
|
||||
void reorderElements(int position, int newPosition);
|
||||
|
||||
void swapElements(int position, int newPosition);
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mariotaku.dynamicgridview;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.mariotaku.twidere.adapter.ArrayAdapter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class DraggableArrayAdapter<T> extends ArrayAdapter<T> implements DraggableAdapter {
|
||||
|
||||
final int INVALID_ID = -1;
|
||||
|
||||
private final HashMap<T, Integer> mIdMap = new HashMap<>();
|
||||
|
||||
public DraggableArrayAdapter(final Context context, final int layoutRes) {
|
||||
this(context, layoutRes, null);
|
||||
}
|
||||
|
||||
public DraggableArrayAdapter(final Context context, final int layoutRes, final Collection<? extends T> collection) {
|
||||
super(context, layoutRes, collection);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final T item) {
|
||||
super.add(item);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(final Collection<? extends T> collection) {
|
||||
super.addAll(collection);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
if (position < 0 || position >= mIdMap.size()) return INVALID_ID;
|
||||
final T item = getItem(position);
|
||||
return mIdMap.get(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAt(final int position) {
|
||||
super.removeAt(position);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll(final Collection<? extends T> collection) {
|
||||
super.removeAll(collection);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reorderElements(final int position, final int newPosition) {
|
||||
final List<T> objects = getObjects();
|
||||
|
||||
T previous = objects.get(position);
|
||||
final int iterator = newPosition < position ? 1 : -1;
|
||||
final int afterPosition = position + iterator;
|
||||
for (int cellPosition = newPosition; cellPosition != afterPosition; cellPosition += iterator) {
|
||||
final T tmp = objects.get(cellPosition);
|
||||
objects.set(cellPosition, previous);
|
||||
previous = tmp;
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(final Comparator<? super T> comparator) {
|
||||
super.sort(comparator);
|
||||
rebuildIdMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swapElements(final int position, final int newPosition) {
|
||||
final List<T> objects = getObjects();
|
||||
final T temp = objects.get(position);
|
||||
objects.set(position, objects.get(newPosition));
|
||||
objects.set(newPosition, temp);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void rebuildIdMap() {
|
||||
mIdMap.clear();
|
||||
final List<T> objects = getObjects();
|
||||
for (int i = 0, j = objects.size(); i < j; ++i) {
|
||||
mIdMap.put(objects.get(i), i);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,648 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
* Copyright (C) 2013 Jacek Marchwicki <jacek.marchwicki@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mariotaku.dynamicgridview;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* The dynamic gridview is an extension of gridview that supports cell dragging
|
||||
* and swapping.
|
||||
*
|
||||
* This layout is in charge of positioning the hover cell in the correct
|
||||
* location on the screen in response to user touch events. It uses the position
|
||||
* of the hover cell to determine when two cells should be swapped. If two cells
|
||||
* should be swapped, all the corresponding data set and layout changes are
|
||||
* handled here.
|
||||
*
|
||||
* If no cell is selected, all the touch events are passed down to the gridview
|
||||
* and behave normally. If one of the items in the gridview experiences a long
|
||||
* press event, the contents of its current visible state are captured as a
|
||||
* bitmap and its visibility is set to INVISIBLE. A hover cell is then created
|
||||
* and added to this layout as an overlaying BitmapDrawable above the gridview.
|
||||
* Once the hover cell is translated some distance to signify an item swap, a
|
||||
* data set change accompanied by animation takes place. When the user releases
|
||||
* the hover cell, it animates into its corresponding position in the gridview.
|
||||
*
|
||||
* When the hover cell is either above or below the bounds of the gridview, this
|
||||
* gridview also scrolls on its own so as to reveal additional content.
|
||||
*/
|
||||
public class DynamicGridView extends GridView {
|
||||
|
||||
private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
|
||||
private final int MOVE_DURATION = 150;
|
||||
private final int LINE_THICKNESS = 15;
|
||||
|
||||
private int mLastEventX = -1;
|
||||
private int mLastEventY = -1;
|
||||
|
||||
private int mDownY = -1;
|
||||
private int mDownX = -1;
|
||||
|
||||
private final int mTotalOffsetX = 0;
|
||||
private int mTotalOffsetY = 0;
|
||||
|
||||
private boolean mCellIsMobile = false;
|
||||
private boolean mIsMobileScrolling = false;
|
||||
private int mSmoothScrollAmountAtEdge = 0;
|
||||
|
||||
private final int INVALID_ID = -1;
|
||||
private long mMobileItemId = INVALID_ID;
|
||||
|
||||
private BitmapDrawable mHoverCell;
|
||||
private Rect mHoverCellCurrentBounds;
|
||||
private Rect mHoverCellOriginalBounds;
|
||||
|
||||
private final int INVALID_POINTER_ID = -1;
|
||||
private int mActivePointerId = INVALID_POINTER_ID;
|
||||
|
||||
private boolean mIsWaitingForScrollFinish = false;
|
||||
private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
|
||||
|
||||
/**
|
||||
* Listens for long clicks on any items in the listview. When a cell has
|
||||
* been selected, the hover cell is created and set up.
|
||||
*/
|
||||
private final OnItemLongClickListener mOnItemLongClickListener = new OnItemLongClickListener() {
|
||||
@Override
|
||||
public boolean onItemLongClick(final AdapterView<?> arg0, final View arg1, final int pos, final long id) {
|
||||
mTotalOffsetY = 0;
|
||||
|
||||
final int position = pointToPosition(mDownX, mDownY);
|
||||
final int itemNum = position - getFirstVisiblePosition();
|
||||
|
||||
final View selectedView = getChildAt(itemNum);
|
||||
mMobileItemId = getAdapter().getItemId(position);
|
||||
mHoverCell = getAndAddHoverView(selectedView);
|
||||
selectedView.setVisibility(INVISIBLE);
|
||||
|
||||
mCellIsMobile = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private final HashMap<Long, Integer> mItemIdTops = new HashMap<>();
|
||||
|
||||
private final HashMap<Long, Integer> mItemIdLefts = new HashMap<>();
|
||||
|
||||
/**
|
||||
* This TypeEvaluator is used to animate the BitmapDrawable back to its
|
||||
* final location when the user lifts his finger by modifying the
|
||||
* BitmapDrawable's bounds.
|
||||
*/
|
||||
private final static TypeEvaluator<Rect> sBoundEvaluator = new TypeEvaluator<Rect>() {
|
||||
@Override
|
||||
public Rect evaluate(final float fraction, final Rect startValue, final Rect endValue) {
|
||||
return new Rect(interpolate(startValue.left, endValue.left, fraction), interpolate(startValue.top,
|
||||
endValue.top, fraction), interpolate(startValue.right, endValue.right, fraction), interpolate(
|
||||
startValue.bottom, endValue.bottom, fraction));
|
||||
}
|
||||
|
||||
public int interpolate(final int start, final int end, final float fraction) {
|
||||
return (int) (start + fraction * (end - start));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This scroll listener is added to the gridview in order to handle cell
|
||||
* swapping when the cell is either at the top or bottom edge of the
|
||||
* gridview. If the hover cell is at either edge of the gridview, the
|
||||
* gridview will begin scrolling. As scrolling takes place, the gridview
|
||||
* continuously checks if new cells became visible and determines whether
|
||||
* they are potential candidates for a cell swap.
|
||||
*/
|
||||
private final OnScrollListener mScrollListener = new OnScrollListener() {
|
||||
|
||||
private int mPreviousFirstVisibleItem = -1;
|
||||
private int mPreviousVisibleItemCount = -1;
|
||||
private int mCurrentFirstVisibleItem;
|
||||
private int mCurrentVisibleItemCount;
|
||||
private int mCurrentScrollState;
|
||||
|
||||
/**
|
||||
* Determines if the gridview scrolled up enough to reveal a new cell at
|
||||
* the top of the list. If so, then the appropriate parameters are
|
||||
* updated.
|
||||
*/
|
||||
public void checkAndHandleFirstVisibleCellChange() {
|
||||
if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
|
||||
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
|
||||
handleCellSwitch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the gridview scrolled down enough to reveal a new cell
|
||||
* at the bottom of the list. If so, then the appropriate parameters are
|
||||
* updated.
|
||||
*/
|
||||
public void checkAndHandleLastVisibleCellChange() {
|
||||
final int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;
|
||||
final int previousLastVisibleItem = mPreviousFirstVisibleItem + mPreviousVisibleItemCount;
|
||||
if (currentLastVisibleItem != previousLastVisibleItem) {
|
||||
if (mCellIsMobile && mMobileItemId != INVALID_ID) {
|
||||
handleCellSwitch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
|
||||
final int totalItemCount) {
|
||||
mCurrentFirstVisibleItem = firstVisibleItem;
|
||||
mCurrentVisibleItemCount = visibleItemCount;
|
||||
|
||||
mPreviousFirstVisibleItem = mPreviousFirstVisibleItem == -1 ? mCurrentFirstVisibleItem
|
||||
: mPreviousFirstVisibleItem;
|
||||
mPreviousVisibleItemCount = mPreviousVisibleItemCount == -1 ? mCurrentVisibleItemCount
|
||||
: mPreviousVisibleItemCount;
|
||||
|
||||
checkAndHandleFirstVisibleCellChange();
|
||||
checkAndHandleLastVisibleCellChange();
|
||||
|
||||
mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
|
||||
mPreviousVisibleItemCount = mCurrentVisibleItemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
|
||||
mCurrentScrollState = scrollState;
|
||||
mScrollState = scrollState;
|
||||
isScrollCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is in charge of invoking 1 of 2 actions. Firstly, if the
|
||||
* gridview is in a state of scrolling invoked by the hover cell being
|
||||
* outside the bounds of the gridview, then this scrolling event is
|
||||
* continued. Secondly, if the hover cell has already been released,
|
||||
* this invokes the animation for the hover cell to return to its
|
||||
* correct position after the gridview has entered an idle scroll state.
|
||||
*/
|
||||
private void isScrollCompleted() {
|
||||
if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {
|
||||
if (mCellIsMobile && mIsMobileScrolling) {
|
||||
handleMobileCellScroll();
|
||||
} else if (mIsWaitingForScrollFinish) {
|
||||
touchEventsEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public DynamicGridView(final Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public DynamicGridView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public DynamicGridView(final Context context, final AttributeSet attrs, final int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context);
|
||||
}
|
||||
|
||||
/** Retrieves the position in the list corresponding to itemID */
|
||||
public int getPositionForID(final long itemID) {
|
||||
final View v = getViewForID(itemID);
|
||||
if (v == null)
|
||||
return -1;
|
||||
else
|
||||
return getPositionForView(v);
|
||||
}
|
||||
|
||||
/** Retrieves the view in the list corresponding to itemID */
|
||||
public View getViewForID(final long itemID) {
|
||||
final int firstVisiblePosition = getFirstVisiblePosition();
|
||||
final ListAdapter adapter = getAdapter();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
final View v = getChildAt(i);
|
||||
final int position = firstVisiblePosition + i;
|
||||
final long id = adapter.getItemId(position);
|
||||
if (id == itemID) return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public View getViewForPosition(final int position) {
|
||||
if (position < 0) return null;
|
||||
if (position >= getCount()) return null;
|
||||
final ListAdapter adapter = getAdapter();
|
||||
final long itemId = adapter.getItemId(position);
|
||||
|
||||
return getViewForID(itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is in charge of determining if the hover cell is above or
|
||||
* below the bounds of the gridview. If so, the gridview does an appropriate
|
||||
* upward or downward smooth scroll so as to reveal new items.
|
||||
*/
|
||||
public boolean handleMobileCellScroll(final Rect r) {
|
||||
final int offset = computeVerticalScrollOffset();
|
||||
final int height = getHeight();
|
||||
final int extent = computeVerticalScrollExtent();
|
||||
final int range = computeVerticalScrollRange();
|
||||
final int hoverViewTop = r.top;
|
||||
final int hoverHeight = r.height();
|
||||
|
||||
if (hoverViewTop <= 0 && offset > 0) {
|
||||
smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hoverViewTop + hoverHeight >= height && offset + extent < range) {
|
||||
smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void init(final Context context) {
|
||||
setOnItemLongClickListener(mOnItemLongClickListener);
|
||||
setOnScrollListener(mScrollListener);
|
||||
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
mSmoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull final MotionEvent event) {
|
||||
|
||||
switch (event.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mDownX = (int) event.getX();
|
||||
mDownY = (int) event.getY();
|
||||
mActivePointerId = event.getPointerId(0);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mActivePointerId == INVALID_POINTER_ID) {
|
||||
break;
|
||||
}
|
||||
|
||||
int pointerIndex = event.findPointerIndex(mActivePointerId);
|
||||
|
||||
mLastEventX = (int) event.getX(pointerIndex);
|
||||
mLastEventY = (int) event.getY(pointerIndex);
|
||||
final int deltaX = mLastEventX - mDownX;
|
||||
final int deltaY = mLastEventY - mDownY;
|
||||
|
||||
if (mCellIsMobile) {
|
||||
mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left + deltaX + mTotalOffsetX,
|
||||
mHoverCellOriginalBounds.top + deltaY + mTotalOffsetY);
|
||||
mHoverCell.setBounds(mHoverCellCurrentBounds);
|
||||
invalidate();
|
||||
|
||||
handleCellSwitch();
|
||||
|
||||
mIsMobileScrolling = false;
|
||||
handleMobileCellScroll();
|
||||
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
touchEventsEnded();
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
touchEventsCancelled();
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
/*
|
||||
* If a multitouch event took place and the original touch
|
||||
* dictating the movement of the hover cell has ended, then the
|
||||
* dragging event ends and the hover cell is animated to its
|
||||
* corresponding position in the gridview.
|
||||
*/
|
||||
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int pointerId = event.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
touchEventsEnded();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapter(final ListAdapter adapter) {
|
||||
if (isInEditMode()) return;
|
||||
if (!(adapter instanceof DraggableAdapter))
|
||||
throw new IllegalArgumentException("Adapter have to implement DraggableAdapter");
|
||||
super.setAdapter(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatchDraw gets invoked when all the child views are about to be drawn.
|
||||
* By overriding this method, the hover cell (BitmapDrawable) can be drawn
|
||||
* over the gridview's items whenever the gridview is redrawn.
|
||||
*/
|
||||
@Override
|
||||
protected void dispatchDraw(@NonNull final Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
if (mHoverCell != null) {
|
||||
mHoverCell.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the hover cell with the appropriate bitmap and of appropriate
|
||||
* size. The hover cell's BitmapDrawable is drawn on top of the bitmap every
|
||||
* single time an invalidate call is made.
|
||||
*/
|
||||
private BitmapDrawable getAndAddHoverView(final View v) {
|
||||
|
||||
final int w = v.getWidth();
|
||||
final int h = v.getHeight();
|
||||
final int top = v.getTop();
|
||||
final int left = v.getLeft();
|
||||
|
||||
final Bitmap b = getBitmapWithBorder(v);
|
||||
|
||||
final BitmapDrawable drawable = new BitmapDrawable(getResources(), b);
|
||||
|
||||
mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
|
||||
mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);
|
||||
|
||||
drawable.setBounds(mHoverCellCurrentBounds);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/** Returns a bitmap showing a screenshot of the view passed in. */
|
||||
private Bitmap getBitmapFromView(final View v) {
|
||||
final Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
v.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/** Draws a black border over the screenshot of the view passed in. */
|
||||
private Bitmap getBitmapWithBorder(final View v) {
|
||||
final Bitmap bitmap = getBitmapFromView(v);
|
||||
final Canvas can = new Canvas(bitmap);
|
||||
|
||||
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
final Paint paint = new Paint();
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(LINE_THICKNESS);
|
||||
paint.setColor(Color.BLACK);
|
||||
|
||||
can.drawBitmap(bitmap, 0, 0, null);
|
||||
can.drawRect(rect, paint);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines whether the hover cell has been shifted far enough
|
||||
* to invoke a cell swap. If so, then the respective cell swap candidate is
|
||||
* determined and the data set is changed. Upon posting a notification of
|
||||
* the data set change, a layout is invoked to place the cells in the right
|
||||
* place. Using a ViewTreeObserver and a corresponding OnPreDrawListener, we
|
||||
* can offset the cell being swapped to where it previously was and then
|
||||
* animate it to its new position.
|
||||
*/
|
||||
private void handleCellSwitch() {
|
||||
final int deltaY = mLastEventY - mDownY;
|
||||
final int deltaX = mLastEventX - mDownX;
|
||||
final int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffsetY + deltaY;
|
||||
final int deltaXTotal = mHoverCellOriginalBounds.left + mTotalOffsetX + deltaX;
|
||||
|
||||
final int numColumns = getNumColumns();
|
||||
final int position = getPositionForID(mMobileItemId);
|
||||
|
||||
final int abovePosition = position - numColumns;
|
||||
final int belowPosition = position + numColumns;
|
||||
final int toLeftPosition = position - 1;
|
||||
final int toRightPosition = position + 1;
|
||||
|
||||
final View aboveView = getViewForPosition(abovePosition);
|
||||
final View belowView = getViewForPosition(belowPosition);
|
||||
View toLeftView = getViewForPosition(toLeftPosition);
|
||||
View toRightView = getViewForPosition(toRightPosition);
|
||||
final View mobileView = getViewForID(mMobileItemId);
|
||||
|
||||
if (toRightView != null && mobileView.getLeft() > toRightView.getLeft()) {
|
||||
// mobile view is far right
|
||||
toRightView = null;
|
||||
}
|
||||
if (toLeftView != null && mobileView.getLeft() < toLeftView.getLeft()) {
|
||||
// mobile view is far left
|
||||
toLeftView = null;
|
||||
}
|
||||
|
||||
final boolean isBelow = belowView != null && deltaYTotal > belowView.getTop();
|
||||
final boolean isAbove = aboveView != null && deltaYTotal < aboveView.getTop();
|
||||
final boolean isToRight = toRightView != null && deltaXTotal > toRightView.getLeft();
|
||||
final boolean isToLeft = toLeftView != null && deltaXTotal < toLeftView.getLeft();
|
||||
int newPosition;
|
||||
if (isBelow) {
|
||||
newPosition = belowPosition;
|
||||
} else if (isAbove) {
|
||||
newPosition = abovePosition;
|
||||
} else if (isToLeft) {
|
||||
newPosition = toLeftPosition;
|
||||
} else if (isToRight) {
|
||||
newPosition = toRightPosition;
|
||||
} else {
|
||||
newPosition = position;
|
||||
}
|
||||
|
||||
if (newPosition == position) return;
|
||||
|
||||
final ListAdapter adapter = getAdapter();
|
||||
final int fromPosition = Math.min(newPosition, position);
|
||||
final int toPosition = Math.max(newPosition, position);
|
||||
|
||||
for (int cellPosition = fromPosition; cellPosition <= toPosition; cellPosition++) {
|
||||
getViewForPosition(cellPosition).setVisibility(View.VISIBLE);
|
||||
}
|
||||
mItemIdLefts.clear();
|
||||
mItemIdTops.clear();
|
||||
|
||||
final int firstVisiblePosition = getFirstVisiblePosition();
|
||||
final int childCount = getChildCount();
|
||||
for (int childAt = 0; childAt < childCount; childAt++) {
|
||||
final View child = getChildAt(childAt);
|
||||
assert child != null;
|
||||
final int pos = firstVisiblePosition + childAt;
|
||||
final long itemId = adapter.getItemId(pos);
|
||||
mItemIdLefts.put(itemId, child.getLeft());
|
||||
mItemIdTops.put(itemId, child.getTop());
|
||||
}
|
||||
|
||||
final ViewTreeObserver observer = getViewTreeObserver();
|
||||
assert observer != null;
|
||||
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
observer.removeOnPreDrawListener(this);
|
||||
final int firstVisiblePosition = getFirstVisiblePosition();
|
||||
final int childCount = getChildCount();
|
||||
for (int childAt = 0; childAt < childCount; childAt++) {
|
||||
final View child = getChildAt(childAt);
|
||||
assert child != null;
|
||||
final int pos = firstVisiblePosition + childAt;
|
||||
final long itemId = adapter.getItemId(pos);
|
||||
if (itemId == mMobileItemId) {
|
||||
child.setVisibility(View.GONE);
|
||||
continue;
|
||||
}
|
||||
final Integer oldLeft = mItemIdLefts.get(itemId);
|
||||
final Integer oldTop = mItemIdTops.get(itemId);
|
||||
if (oldLeft == null) {
|
||||
continue;
|
||||
}
|
||||
mItemIdLefts.put(itemId, child.getLeft());
|
||||
mItemIdTops.put(itemId, child.getTop());
|
||||
|
||||
final int newLeft = child.getLeft();
|
||||
final int newTop = child.getTop();
|
||||
|
||||
final int deltaX = oldLeft - newLeft;
|
||||
final int deltaY = oldTop - newTop;
|
||||
|
||||
if (deltaX == 0 && deltaY == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child.setTranslationX(deltaX);
|
||||
child.setTranslationY(deltaY);
|
||||
|
||||
final AnimatorSet animator = new AnimatorSet();
|
||||
animator.playTogether(ObjectAnimator.ofFloat(child, View.TRANSLATION_X, 0),
|
||||
ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 0));
|
||||
animator.setDuration(MOVE_DURATION).start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
((DraggableAdapter) adapter).reorderElements(position, newPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this gridview is in a scrolling state invoked by the
|
||||
* fact that the hover cell is out of the bounds of the gridview;
|
||||
*/
|
||||
private void handleMobileCellScroll() {
|
||||
mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all the appropriate fields to a default state.
|
||||
*/
|
||||
private void touchEventsCancelled() {
|
||||
final View mobileView = getViewForID(mMobileItemId);
|
||||
if (mCellIsMobile) {
|
||||
mMobileItemId = INVALID_ID;
|
||||
mobileView.setVisibility(VISIBLE);
|
||||
mHoverCell = null;
|
||||
invalidate();
|
||||
}
|
||||
mCellIsMobile = false;
|
||||
mIsMobileScrolling = false;
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all the appropriate fields to a default state while also animating
|
||||
* the hover cell back to its correct location.
|
||||
*/
|
||||
private void touchEventsEnded() {
|
||||
final View mobileView = getViewForID(mMobileItemId);
|
||||
if (mCellIsMobile || mIsWaitingForScrollFinish) {
|
||||
mCellIsMobile = false;
|
||||
mIsWaitingForScrollFinish = false;
|
||||
mIsMobileScrolling = false;
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
|
||||
// If the autoscroller has not completed scrolling, we need to wait
|
||||
// for it to
|
||||
// finish in order to determine the final location of where the
|
||||
// hover cell
|
||||
// should be animated to.
|
||||
if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
mIsWaitingForScrollFinish = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mHoverCellCurrentBounds.offsetTo(mobileView.getLeft(), mobileView.getTop());
|
||||
|
||||
final ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds", sBoundEvaluator,
|
||||
mHoverCellCurrentBounds);
|
||||
hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(final ValueAnimator valueAnimator) {
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(final Animator animation) {
|
||||
mMobileItemId = INVALID_ID;
|
||||
mobileView.setVisibility(VISIBLE);
|
||||
mHoverCell = null;
|
||||
setEnabled(true);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(final Animator animation) {
|
||||
setEnabled(false);
|
||||
}
|
||||
});
|
||||
hoverViewAnimator.start();
|
||||
} else {
|
||||
touchEventsCancelled();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import android.content.SharedPreferences;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.Rect;
|
||||
import android.location.Criteria;
|
||||
|
@ -61,6 +62,7 @@ import android.support.v7.widget.RecyclerView;
|
|||
import android.support.v7.widget.RecyclerView.ItemDecoration;
|
||||
import android.support.v7.widget.RecyclerView.State;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
|
@ -84,7 +86,6 @@ import android.view.View.OnClickListener;
|
|||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -95,8 +96,8 @@ import com.github.johnpersano.supertoasts.SuperToast.OnDismissListener;
|
|||
import com.nostra13.universalimageloader.utils.IoUtils;
|
||||
import com.twitter.Extractor;
|
||||
|
||||
import org.mariotaku.dynamicgridview.DraggableArrayAdapter;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.adapter.ArrayRecyclerAdapter;
|
||||
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter;
|
||||
import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment;
|
||||
import org.mariotaku.twidere.fragment.support.SupportProgressDialogFragment;
|
||||
|
@ -119,7 +120,6 @@ import org.mariotaku.twidere.util.ContentValuesCreator;
|
|||
import org.mariotaku.twidere.util.EditTextEnterHandler;
|
||||
import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener;
|
||||
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
|
||||
import org.mariotaku.twidere.util.MathUtils;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.MenuUtils;
|
||||
import org.mariotaku.twidere.util.ParseUtils;
|
||||
|
@ -134,6 +134,7 @@ import org.mariotaku.twidere.view.BadgeView;
|
|||
import org.mariotaku.twidere.view.ComposeEditText;
|
||||
import org.mariotaku.twidere.view.ShapedImageView;
|
||||
import org.mariotaku.twidere.view.StatusTextCountView;
|
||||
import org.mariotaku.twidere.view.helper.SimpleItemTouchHelperCallback;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -167,7 +168,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
private SupportMenuInflater mMenuInflater;
|
||||
|
||||
// Views
|
||||
private GridView mMediaPreviewGrid;
|
||||
private RecyclerView mAttachedMediaPreview;
|
||||
private ActionMenuView mMenuBar;
|
||||
private ComposeEditText mEditText;
|
||||
private View mSendView;
|
||||
|
@ -199,6 +200,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
private SetProgressVisibleRunnable mSetProgressVisibleRunnable;
|
||||
private boolean mFragmentResumed;
|
||||
private int mKeyMetaState;
|
||||
private ItemTouchHelper mItemTouchHelper;
|
||||
|
||||
@Override
|
||||
public int getThemeColor() {
|
||||
|
@ -524,7 +526,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
public void onContentChanged() {
|
||||
super.onContentChanged();
|
||||
mEditText = (ComposeEditText) findViewById(R.id.edit_text);
|
||||
mMediaPreviewGrid = (GridView) findViewById(R.id.media_thumbnail_preview);
|
||||
mAttachedMediaPreview = (RecyclerView) findViewById(R.id.attached_media_preview);
|
||||
mMenuBar = (ActionMenuView) findViewById(R.id.menu_bar);
|
||||
mSendView = findViewById(R.id.send);
|
||||
mSendTextCountView = (StatusTextCountView) mSendView.findViewById(R.id.status_text_count);
|
||||
|
@ -549,7 +551,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
|
||||
public void removeAllMedia(final List<ParcelableMediaUpdate> list) {
|
||||
mMediaPreviewAdapter.removeAll(list);
|
||||
updateMediaPreview();
|
||||
}
|
||||
|
||||
public void saveToDrafts() {
|
||||
|
@ -621,8 +622,26 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
mAccountSelector.setAdapter(mAccountsAdapter);
|
||||
mAccountsAdapter.setAccounts(ParcelableCredentials.getCredentialsArray(this, false, false));
|
||||
|
||||
mMediaPreviewAdapter = new MediaPreviewAdapter(this);
|
||||
mMediaPreviewGrid.setAdapter(mMediaPreviewAdapter);
|
||||
|
||||
mMediaPreviewAdapter = new MediaPreviewAdapter(this, new SimpleItemTouchHelperCallback.OnStartDragListener() {
|
||||
@Override
|
||||
public void onStartDrag(ViewHolder viewHolder) {
|
||||
mItemTouchHelper.startDrag(viewHolder);
|
||||
}
|
||||
});
|
||||
mItemTouchHelper = new ItemTouchHelper(new AttachedMediaItemTouchHelperCallback(mMediaPreviewAdapter));
|
||||
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||
mAttachedMediaPreview.setLayoutManager(layoutManager);
|
||||
mAttachedMediaPreview.setAdapter(mMediaPreviewAdapter);
|
||||
mItemTouchHelper.attachToRecyclerView(mAttachedMediaPreview);
|
||||
final int previewGridSpacing = getResources().getDimensionPixelSize(R.dimen.element_spacing_small);
|
||||
mAttachedMediaPreview.addItemDecoration(new ItemDecoration() {
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
|
||||
outRect.left = outRect.right = previewGridSpacing;
|
||||
}
|
||||
});
|
||||
|
||||
final Intent intent = getIntent();
|
||||
|
||||
|
@ -679,10 +698,11 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
setMenu();
|
||||
updateLocationState();
|
||||
updateMediaPreview();
|
||||
notifyAccountSelectionChanged();
|
||||
|
||||
mTextChanged = false;
|
||||
|
||||
updateAttachedMediaView();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -794,17 +814,23 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
|
||||
private void addMedia(final ParcelableMediaUpdate media) {
|
||||
mMediaPreviewAdapter.add(media);
|
||||
updateMediaPreview();
|
||||
updateAttachedMediaView();
|
||||
}
|
||||
|
||||
|
||||
private void addMedia(final List<ParcelableMediaUpdate> media) {
|
||||
mMediaPreviewAdapter.addAll(media);
|
||||
updateMediaPreview();
|
||||
updateAttachedMediaView();
|
||||
}
|
||||
|
||||
private void clearMedia() {
|
||||
mMediaPreviewAdapter.clear();
|
||||
updateMediaPreview();
|
||||
updateAttachedMediaView();
|
||||
}
|
||||
|
||||
private void updateAttachedMediaView() {
|
||||
mAttachedMediaPreview.setVisibility(hasMedia() ? View.VISIBLE : View.GONE);
|
||||
setMenu();
|
||||
}
|
||||
|
||||
private Uri createTempImageUri() {
|
||||
|
@ -924,7 +950,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
if (user == null || user.id <= 0) return false;
|
||||
final String my_screen_name = Utils.getAccountScreenName(this, user.account_id);
|
||||
if (TextUtils.isEmpty(my_screen_name)) return false;
|
||||
mEditText.setText("@" + user.screen_name + " ");
|
||||
mEditText.setText(String.format("@%s ", user.screen_name));
|
||||
final int selection_end = mEditText.length();
|
||||
mEditText.setSelection(selection_end);
|
||||
mAccountsAdapter.setSelectedAccountIds(user.account_id);
|
||||
|
@ -985,7 +1011,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
|
||||
private boolean hasMedia() {
|
||||
return !mMediaPreviewAdapter.isEmpty();
|
||||
return mMediaPreviewAdapter.getItemCount() > 0;
|
||||
}
|
||||
|
||||
private boolean isQuote() {
|
||||
|
@ -1076,7 +1102,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
MenuUtils.setMenuItemAvailability(menu, R.id.view, hasInReplyTo);
|
||||
MenuUtils.setMenuItemAvailability(menu, R.id.media_menu, hasMedia || hasInReplyTo);
|
||||
MenuUtils.setMenuItemAvailability(menu, R.id.toggle_sensitive, hasMedia);
|
||||
MenuUtils.setMenuItemAvailability(menu, R.id.edit_media, hasMedia);
|
||||
MenuUtils.setMenuItemAvailability(menu, R.id.link_to_quoted_status, isQuote());
|
||||
MenuUtils.setMenuItemAvailability(menu, R.id.schedule, isScheduleSupported());
|
||||
|
||||
|
@ -1133,30 +1158,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
}
|
||||
|
||||
private static class SetProgressVisibleRunnable implements Runnable {
|
||||
|
||||
private final ComposeActivity activity;
|
||||
private final boolean visible;
|
||||
|
||||
SetProgressVisibleRunnable(ComposeActivity activity, boolean visible) {
|
||||
this.activity = activity;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final FragmentManager fm = activity.getSupportFragmentManager();
|
||||
final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
|
||||
if (!visible && f instanceof DialogFragment) {
|
||||
((DialogFragment) f).dismiss();
|
||||
} else if (visible) {
|
||||
SupportProgressDialogFragment df = new SupportProgressDialogFragment();
|
||||
df.show(fm, DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
|
||||
df.setCancelable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setRecentLocation(ParcelableLocation location) {
|
||||
if (location != null) {
|
||||
mLocationText.setText(location.getHumanReadableString(3));
|
||||
|
@ -1238,13 +1239,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
}
|
||||
|
||||
private void updateMediaPreview() {
|
||||
final int count = mMediaPreviewAdapter.getCount();
|
||||
final Resources res = getResources();
|
||||
final int maxColumns = res.getInteger(R.integer.grid_column_image_preview);
|
||||
mMediaPreviewGrid.setNumColumns(MathUtils.clamp(count, maxColumns, 1));
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
if (isFinishing()) return;
|
||||
final boolean hasMedia = hasMedia();
|
||||
|
@ -1303,6 +1297,30 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
mSendTextCountView.setTextCount(validatedCount);
|
||||
}
|
||||
|
||||
private static class SetProgressVisibleRunnable implements Runnable {
|
||||
|
||||
private final ComposeActivity activity;
|
||||
private final boolean visible;
|
||||
|
||||
SetProgressVisibleRunnable(ComposeActivity activity, boolean visible) {
|
||||
this.activity = activity;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final FragmentManager fm = activity.getSupportFragmentManager();
|
||||
final Fragment f = fm.findFragmentByTag(DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
|
||||
if (!visible && f instanceof DialogFragment) {
|
||||
((DialogFragment) f).dismiss();
|
||||
} else if (visible) {
|
||||
SupportProgressDialogFragment df = new SupportProgressDialogFragment();
|
||||
df.show(fm, DISCARD_STATUS_DIALOG_FRAGMENT_TAG);
|
||||
df.setCancelable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class AccountIconViewHolder extends ViewHolder implements OnClickListener {
|
||||
|
||||
private final AccountIconsAdapter adapter;
|
||||
|
@ -1540,7 +1558,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
if (mMedia == null) return false;
|
||||
try {
|
||||
for (final ParcelableMediaUpdate media : mMedia) {
|
||||
if (media.uri == null) continue;
|
||||
final Uri uri = Uri.parse(media.uri);
|
||||
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||
final File file = new File(uri.getPath());
|
||||
|
@ -1582,7 +1599,6 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
@Override
|
||||
protected Object doInBackground(final Object... params) {
|
||||
for (final ParcelableMediaUpdate media : mActivity.getMediaList()) {
|
||||
if (media.uri == null) continue;
|
||||
final Uri uri = Uri.parse(media.uri);
|
||||
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
|
||||
final File file = new File(uri.getPath());
|
||||
|
@ -1606,28 +1622,77 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
}
|
||||
|
||||
private static class MediaPreviewAdapter extends DraggableArrayAdapter<ParcelableMediaUpdate> {
|
||||
public static class MediaPreviewAdapter extends ArrayRecyclerAdapter<ParcelableMediaUpdate, MediaPreviewViewHolder>
|
||||
implements SimpleItemTouchHelperCallback.ItemTouchHelperAdapter {
|
||||
|
||||
private final MediaLoaderWrapper mImageLoader;
|
||||
private final LayoutInflater mInflater;
|
||||
|
||||
public MediaPreviewAdapter(final ComposeActivity activity) {
|
||||
super(activity, R.layout.grid_item_media_editor);
|
||||
mImageLoader = activity.mImageLoader;
|
||||
private final SimpleItemTouchHelperCallback.OnStartDragListener mDragStartListener;
|
||||
|
||||
public MediaPreviewAdapter(final ComposeActivity activity, SimpleItemTouchHelperCallback.OnStartDragListener dragStartListener) {
|
||||
super(activity);
|
||||
mInflater = LayoutInflater.from(activity);
|
||||
mDragStartListener = dragStartListener;
|
||||
}
|
||||
|
||||
public void onStartDrag(ViewHolder viewHolder) {
|
||||
mDragStartListener.onStartDrag(viewHolder);
|
||||
}
|
||||
|
||||
public List<ParcelableMediaUpdate> getAsList() {
|
||||
return Collections.unmodifiableList(getObjects());
|
||||
return Collections.unmodifiableList(mData);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(MediaPreviewViewHolder holder, int position, ParcelableMediaUpdate item) {
|
||||
final ParcelableMediaUpdate media = getItem(position);
|
||||
holder.displayMedia(this, media);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||
final View view = super.getView(position, convertView, parent);
|
||||
final ParcelableMediaUpdate media = getItem(position);
|
||||
final ImageView image = (ImageView) view.findViewById(R.id.image);
|
||||
mImageLoader.displayPreviewImage(media.uri, image);
|
||||
return view;
|
||||
public MediaPreviewViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
final View view = mInflater.inflate(R.layout.grid_item_media_editor, parent, false);
|
||||
return new MediaPreviewViewHolder(this, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDismiss(int position) {
|
||||
mData.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
|
||||
((ComposeActivity) getContext()).updateAttachedMediaView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemMove(int fromPosition, int toPosition) {
|
||||
Collections.swap(mData, fromPosition, toPosition);
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class MediaPreviewViewHolder extends ViewHolder implements OnLongClickListener {
|
||||
|
||||
private final ImageView image;
|
||||
private final MediaPreviewAdapter adapter;
|
||||
|
||||
public MediaPreviewViewHolder(MediaPreviewAdapter adapter, View itemView) {
|
||||
super(itemView);
|
||||
this.adapter = adapter;
|
||||
itemView.setOnLongClickListener(this);
|
||||
image = (ImageView) itemView.findViewById(R.id.image);
|
||||
}
|
||||
|
||||
public void displayMedia(MediaPreviewAdapter adapter, ParcelableMediaUpdate media) {
|
||||
adapter.getMediaLoader().displayPreviewImage(media.uri, image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
adapter.onStartDrag(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1683,4 +1748,47 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
|
|||
}
|
||||
}
|
||||
|
||||
public static class AttachedMediaItemTouchHelperCallback extends SimpleItemTouchHelperCallback {
|
||||
public static final float ALPHA_FULL = 1.0f;
|
||||
|
||||
public AttachedMediaItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||
super(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
|
||||
// Set movement flags based on the layout manager
|
||||
final int dragFlags = ItemTouchHelper.START | ItemTouchHelper.END;
|
||||
final int swipeFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
// Fade out the view as it is swiped out of the parent's bounds
|
||||
final float alpha = ALPHA_FULL - Math.abs(dY) / (float) viewHolder.itemView.getHeight();
|
||||
viewHolder.itemView.setAlpha(alpha);
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
} else {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
viewHolder.itemView.setAlpha(ALPHA_FULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.mariotaku.twidere.adapter;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.widget.CardView;
|
||||
|
@ -39,13 +38,10 @@ import org.mariotaku.twidere.fragment.support.UserFragment;
|
|||
import org.mariotaku.twidere.model.ParcelableActivity;
|
||||
import org.mariotaku.twidere.model.ParcelableMedia;
|
||||
import org.mariotaku.twidere.model.ParcelableStatus;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoadingHandler;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
import org.mariotaku.twidere.util.TwidereLinkify;
|
||||
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener;
|
||||
import org.mariotaku.twidere.util.UserColorNameManager;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.view.holder.ActivityTitleSummaryViewHolder;
|
||||
import org.mariotaku.twidere.view.holder.GapViewHolder;
|
||||
|
@ -99,12 +95,6 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
|
|||
@Override
|
||||
public abstract void setData(Data data);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaLoadingHandler getMediaLoadingHandler() {
|
||||
return mLoadingHandler;
|
||||
|
@ -120,12 +110,6 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
|
|||
return mStatusAdapterDelegate.getMediaPreviewStyle();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getTextSize() {
|
||||
return mStatusAdapterDelegate.getTextSize();
|
||||
|
@ -335,12 +319,6 @@ public abstract class AbsActivitiesAdapter<Data> extends LoadMoreSupportAdapter<
|
|||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
public void setListener(ActivityAdapterListener listener) {
|
||||
mActivityAdapterListener = listener;
|
||||
}
|
||||
|
|
|
@ -90,11 +90,6 @@ public abstract class AbsStatusesAdapter<D> extends LoadMoreSupportAdapter<ViewH
|
|||
return mShowAccountsColor;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final MediaLoadingHandler getMediaLoadingHandler() {
|
||||
|
@ -111,18 +106,6 @@ public abstract class AbsStatusesAdapter<D> extends LoadMoreSupportAdapter<ViewH
|
|||
return mMediaPreviewStyle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final float getTextSize() {
|
||||
return mTextSize;
|
||||
|
|
|
@ -74,17 +74,6 @@ public abstract class AbsUserListsAdapter<D> extends LoadMoreSupportAdapter<View
|
|||
return mTextSize;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileImageEnabled() {
|
||||
|
@ -177,12 +166,6 @@ public abstract class AbsUserListsAdapter<D> extends LoadMoreSupportAdapter<View
|
|||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
protected abstract void bindUserList(UserListViewHolder holder, int position);
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.mariotaku.twidere.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -30,10 +29,7 @@ import android.view.ViewGroup;
|
|||
import org.mariotaku.twidere.Constants;
|
||||
import org.mariotaku.twidere.R;
|
||||
import org.mariotaku.twidere.adapter.iface.IUsersAdapter;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
import org.mariotaku.twidere.util.UserColorNameManager;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder;
|
||||
import org.mariotaku.twidere.view.holder.UserViewHolder;
|
||||
|
@ -71,18 +67,6 @@ public abstract class AbsUsersAdapter<D> extends LoadMoreSupportAdapter<ViewHold
|
|||
return mTextSize;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileImageEnabled() {
|
||||
return mDisplayProfileImage;
|
||||
|
@ -169,12 +153,6 @@ public abstract class AbsUsersAdapter<D> extends LoadMoreSupportAdapter<ViewHold
|
|||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
protected abstract void bindUser(UserViewHolder holder, int position);
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.mariotaku.twidere.adapter;
|
||||
|
||||
import android.support.v7.widget.RecyclerView.Adapter;
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -12,10 +12,14 @@ import java.util.List;
|
|||
/**
|
||||
* Created by mariotaku on 14/10/27.
|
||||
*/
|
||||
public abstract class ArrayRecyclerAdapter<T, H extends ViewHolder> extends Adapter<H> {
|
||||
public abstract class ArrayRecyclerAdapter<T, H extends ViewHolder> extends BaseRecyclerViewAdapter<H> {
|
||||
|
||||
protected final ArrayList<T> mData = new ArrayList<>();
|
||||
|
||||
public ArrayRecyclerAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onBindViewHolder(H holder, int position) {
|
||||
onBindViewHolder(holder, position, getItem(position));
|
||||
|
|
|
@ -52,6 +52,7 @@ public abstract class BaseRecyclerViewAdapter<VH extends RecyclerView.ViewHolder
|
|||
protected UserColorNameManager mUserColorNameManager;
|
||||
@Inject
|
||||
protected SharedPreferencesWrapper mPreferences;
|
||||
|
||||
public BaseRecyclerViewAdapter(Context context) {
|
||||
mContext = context;
|
||||
//noinspection unchecked
|
||||
|
@ -65,4 +66,28 @@ public abstract class BaseRecyclerViewAdapter<VH extends RecyclerView.ViewHolder
|
|||
public final Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public final SharedPreferencesWrapper getPreferences() {
|
||||
return mPreferences;
|
||||
}
|
||||
|
||||
public final UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
public final MultiSelectManager getMultiSelectManager() {
|
||||
return mMultiSelectManager;
|
||||
}
|
||||
|
||||
public final MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
public final ReadStateManager getReadStateManager() {
|
||||
return mReadStateManager;
|
||||
}
|
||||
|
||||
public final AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.mariotaku.twidere.adapter.iface.IDirectMessagesAdapter;
|
|||
import org.mariotaku.twidere.model.ParcelableDirectMessage;
|
||||
import org.mariotaku.twidere.model.ParcelableDirectMessage.CursorIndices;
|
||||
import org.mariotaku.twidere.util.DirectMessageOnLinkClickHandler;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoadingHandler;
|
||||
import org.mariotaku.twidere.util.ThemeUtils;
|
||||
import org.mariotaku.twidere.util.TwidereLinkify;
|
||||
|
@ -75,11 +74,6 @@ public class MessageConversationAdapter extends BaseRecyclerViewAdapter<ViewHold
|
|||
mOutgoingMessageColor = ThemeUtils.getCardBackgroundColor(context, ThemeUtils.getThemeBackgroundOption(context), ThemeUtils.getUserThemeBackgroundAlpha(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
public MediaLoadingHandler getMediaLoadingHandler() {
|
||||
return mMediaLoadingHandler;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -35,10 +34,7 @@ import org.mariotaku.twidere.R;
|
|||
import org.mariotaku.twidere.adapter.iface.IContentCardAdapter;
|
||||
import org.mariotaku.twidere.model.StringLongPair;
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.ReadStateManager.OnReadStateChangeListener;
|
||||
import org.mariotaku.twidere.util.UserColorNameManager;
|
||||
import org.mariotaku.twidere.util.Utils;
|
||||
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder;
|
||||
import org.mariotaku.twidere.view.holder.MessageEntryViewHolder;
|
||||
|
@ -87,12 +83,6 @@ public class MessageEntriesAdapter extends LoadMoreSupportAdapter<ViewHolder> im
|
|||
return mTextSize;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mTwitterWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileImageEnabled() {
|
||||
return mDisplayProfileImage;
|
||||
|
@ -104,18 +94,6 @@ public class MessageEntriesAdapter extends LoadMoreSupportAdapter<ViewHolder> im
|
|||
return new DirectMessageEntry(c);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(final View view) {
|
||||
// if (mMultiSelectManager.isActive()) return;
|
||||
|
|
|
@ -1262,23 +1262,11 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
return mTextSize;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AsyncTwitterWrapper getTwitterWrapper() {
|
||||
return mFragment.mTwitterWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileImageEnabled() {
|
||||
return mDisplayProfileImage;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaLoaderWrapper getMediaLoader() {
|
||||
return mMediaLoader;
|
||||
}
|
||||
|
||||
public StatusFragment getFragment() {
|
||||
return mFragment;
|
||||
}
|
||||
|
@ -1390,12 +1378,6 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
|||
return mMediaLoadingHandler;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserColorNameManager getUserColorNameManager() {
|
||||
return mUserColorNameManager;
|
||||
}
|
||||
|
||||
public ParcelableStatus getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.support.v7.widget.RecyclerView;
|
|||
import org.mariotaku.twidere.activity.BasePreferenceActivity;
|
||||
import org.mariotaku.twidere.activity.BaseThemedActivity;
|
||||
import org.mariotaku.twidere.activity.support.BaseAppCompatActivity;
|
||||
import org.mariotaku.twidere.activity.support.ComposeActivity;
|
||||
import org.mariotaku.twidere.activity.support.ThemedFragmentActivity;
|
||||
import org.mariotaku.twidere.adapter.AccountsAdapter;
|
||||
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
|
||||
|
@ -106,4 +107,5 @@ public interface GeneralComponent {
|
|||
void inject(BaseFiltersFragment.FilteredUsersFragment.FilterUsersListAdapter object);
|
||||
|
||||
void inject(AccountsDashboardFragment.OptionItemsAdapter object);
|
||||
|
||||
}
|
||||
|
|
|
@ -43,28 +43,13 @@ public class InetAddressUtils {
|
|||
}
|
||||
|
||||
private InetAddressUtils() {
|
||||
throw new AssertionError("Trying to instantiate this class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv4 address
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid IPv4 address
|
||||
* @param input IP address in string
|
||||
* @return type corresponding to <sys/socket.h>
|
||||
*/
|
||||
public static boolean isIPv4Address(final String input) {
|
||||
return getInetAddressType(input) == 2; // AF_INET4
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the parameter is a valid IPv6 address (including compressed).
|
||||
*
|
||||
* @param input the address string to check for validity
|
||||
* @return true if the input parameter is a valid standard or compressed IPv6 address
|
||||
*/
|
||||
public static boolean isIPv6Address(final String input) {
|
||||
return getInetAddressType(input) == 10; // AF_INET6
|
||||
}
|
||||
|
||||
public native static int getInetAddressType(final String input);
|
||||
|
||||
public native static InetAddress getResolvedIPAddress(@Nullable final String host, @NonNull final String address);
|
||||
|
|
|
@ -79,12 +79,6 @@ public class TwidereDns implements Constants, Dns {
|
|||
|
||||
@NonNull
|
||||
private InetAddress[] resolveInternal(String originalHost, String host) throws IOException {
|
||||
try {
|
||||
Log.d(LOGTAG, "Test Resolved " + Arrays.toString(mResolver.resolve("localhost")));
|
||||
Log.d(LOGTAG, "Test Resolved " + Arrays.toString(mResolver.resolve("ip6-localhost")));
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (isValidIpAddress(host)) return fromAddressString(originalHost, host);
|
||||
// First, I'll try to load address cached.
|
||||
final InetAddress[] cachedHostAddr = mHostCache.get(host);
|
||||
|
@ -197,7 +191,7 @@ public class TwidereDns implements Constants, Dns {
|
|||
}
|
||||
|
||||
private static boolean isValidIpAddress(final String address) {
|
||||
return InetAddressUtils.isIPv4Address(address) || InetAddressUtils.isIPv6Address(address);
|
||||
return InetAddressUtils.getInetAddressType(address) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.view.helper;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
|
||||
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
|
||||
* </br/>
|
||||
* Expects the <code>RecyclerView.Adapter</code> to listen for {@link
|
||||
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
|
||||
* {@link ItemTouchHelperViewHolder}.
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public abstract class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||
protected final ItemTouchHelperAdapter mAdapter;
|
||||
|
||||
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
|
||||
if (source.getItemViewType() != target.getItemViewType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notify the adapter of the move
|
||||
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
// Notify the adapter of the dismissal
|
||||
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||
// We only want the active item to change
|
||||
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
|
||||
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||
// Let the view holder know that this item is being moved or dragged
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemSelected();
|
||||
}
|
||||
}
|
||||
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||
// Tell the view holder it's time to restore the idle state
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemClear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to notify an item ViewHolder of relevant callbacks from {@link
|
||||
* ItemTouchHelper.Callback}.
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public interface ItemTouchHelperViewHolder {
|
||||
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
|
||||
* Implementations should update the item view to indicate it's active state.
|
||||
*/
|
||||
void onItemSelected();
|
||||
|
||||
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
|
||||
* state should be cleared.
|
||||
*/
|
||||
void onItemClear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public interface ItemTouchHelperAdapter {
|
||||
|
||||
/**
|
||||
* Called when an item has been dragged far enough to trigger a move. This is called every time
|
||||
* an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
|
||||
* <br/>
|
||||
* Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
|
||||
* adjusting the underlying data to reflect this move.
|
||||
*
|
||||
* @param fromPosition The start position of the moved item.
|
||||
* @param toPosition Then resolved position of the moved item.
|
||||
* @return True if the item was moved to the new adapter position.
|
||||
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||
*/
|
||||
boolean onItemMove(int fromPosition, int toPosition);
|
||||
|
||||
|
||||
/**
|
||||
* Called when an item has been dismissed by a swipe.<br/>
|
||||
* <br/>
|
||||
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after
|
||||
* adjusting the underlying data to reflect this removal.
|
||||
*
|
||||
* @param position The position of the item dismissed.
|
||||
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||
*/
|
||||
void onItemDismiss(int position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for manual initiation of a drag.
|
||||
*/
|
||||
public interface OnStartDragListener {
|
||||
|
||||
/**
|
||||
* Called when a view is requesting a start of a drag.
|
||||
*
|
||||
* @param viewHolder The holder of the view to drag.
|
||||
*/
|
||||
void onStartDrag(RecyclerView.ViewHolder viewHolder);
|
||||
|
||||
}
|
||||
}
|
|
@ -31,21 +31,6 @@
|
|||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<org.mariotaku.dynamicgridview.DynamicGridView
|
||||
android:id="@+id/media_thumbnail_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@+id/edit_text_container"
|
||||
android:layout_alignEnd="@+id/edit_text_container"
|
||||
android:layout_alignLeft="@+id/edit_text_container"
|
||||
android:layout_alignRight="@+id/edit_text_container"
|
||||
android:layout_alignStart="@+id/edit_text_container"
|
||||
android:layout_alignTop="@+id/edit_text_container"
|
||||
android:alpha="0.2"
|
||||
android:numColumns="@integer/grid_column_image_preview"
|
||||
android:stretchMode="columnWidth"
|
||||
tools:listitem="@layout/gallery_item_image_preview" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/edit_text_container"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -66,6 +51,14 @@
|
|||
android:scrollbars="vertical"
|
||||
android:singleLine="false" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/attached_media_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:layout_weight="0"
|
||||
tools:listitem="@layout/grid_item_media_editor" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/location_container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Twidere - Twitter client for Android
|
||||
~
|
||||
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
|
@ -18,9 +17,16 @@
|
|||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<org.mariotaku.twidere.view.SquareHighlightImageView
|
||||
android:id="@+id/image"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"/>
|
||||
<org.mariotaku.twidere.view.SquareFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout_height="128dp">
|
||||
|
||||
<org.mariotaku.twidere.view.HighlightImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
</org.mariotaku.twidere.view.SquareFrameLayout>
|
|
@ -31,10 +31,6 @@
|
|||
android:id="@+id/add_image_sub_item"
|
||||
android:icon="@drawable/ic_action_gallery"
|
||||
android:title="@string/add_image" />
|
||||
<item
|
||||
android:id="@id/edit_media"
|
||||
android:icon="@drawable/ic_action_edit"
|
||||
android:title="@string/edit_media" />
|
||||
<item
|
||||
android:id="@id/toggle_sensitive"
|
||||
android:checkable="true"
|
||||
|
|
Loading…
Reference in New Issue