diff --git a/Clover/app/src/main/AndroidManifest.xml b/Clover/app/src/main/AndroidManifest.xml index 5ebdb2af..75d89b03 100644 --- a/Clover/app/src/main/AndroidManifest.xml +++ b/Clover/app/src/main/AndroidManifest.xml @@ -24,26 +24,22 @@ along with this program. If not, see . + android:theme="@style/Chan.Theme"> + + android:configChanges="keyboardHidden|orientation|screenSize" + android:label="@string/app_name"> - - - - + @@ -76,48 +72,15 @@ along with this program. If not, see . android:scheme="https" /> - - - - - - - - - - - - - + + + + + diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortController.java b/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortController.java deleted file mode 100644 index 008dcd32..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortController.java +++ /dev/null @@ -1,469 +0,0 @@ -package com.mobeta.android.dslv; - -import android.graphics.Point; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.widget.AdapterView; - -/** - * Class that starts and stops item drags on a {@link DragSortListView} based on - * touch gestures. This class also inherits from {@link SimpleFloatViewManager}, - * which provides basic float View creation. - * - * An instance of this class is meant to be passed to the methods - * {@link DragSortListView#setTouchListener()} and - * {@link DragSortListView#setFloatViewManager()} of your - * {@link DragSortListView} instance. - */ -public class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener, - GestureDetector.OnGestureListener { - - /** - * Drag init mode enum. - */ - public static final int ON_DOWN = 0; - public static final int ON_DRAG = 1; - public static final int ON_LONG_PRESS = 2; - - private int mDragInitMode = ON_DOWN; - - private boolean mSortEnabled = true; - - /** - * Remove mode enum. - */ - public static final int CLICK_REMOVE = 0; - public static final int FLING_REMOVE = 1; - - /** - * The current remove mode. - */ - private int mRemoveMode; - - private boolean mRemoveEnabled = false; - private boolean mIsRemoving = false; - - private GestureDetector mDetector; - - private GestureDetector mFlingRemoveDetector; - - private int mTouchSlop; - - public static final int MISS = -1; - - private int mHitPos = MISS; - private int mFlingHitPos = MISS; - - private int mClickRemoveHitPos = MISS; - - private int[] mTempLoc = new int[2]; - - private int mItemX; - private int mItemY; - - private int mCurrX; - private int mCurrY; - - private boolean mDragging = false; - - private float mFlingSpeed = 500f; - - private int mDragHandleId; - - private int mClickRemoveId; - - private int mFlingHandleId; - private boolean mCanDrag; - - private DragSortListView mDslv; - private int mPositionX; - - /** - * Calls {@link #DragSortController(DragSortListView, int)} with a 0 drag - * handle id, FLING_RIGHT_REMOVE remove mode, and ON_DOWN drag init. By - * default, sorting is enabled, and removal is disabled. - * - * @param dslv - * The DSLV instance - */ - public DragSortController(DragSortListView dslv) { - this(dslv, 0, ON_DOWN, FLING_REMOVE); - } - - public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode) { - this(dslv, dragHandleId, dragInitMode, removeMode, 0); - } - - public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, - int clickRemoveId) { - this(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0); - } - - /** - * By default, sorting is enabled, and removal is disabled. - * - * @param dslv - * The DSLV instance - * @param dragHandleId - * The resource id of the View that represents the drag handle in - * a list item. - */ - public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, - int clickRemoveId, int flingHandleId) { - super(dslv); - mDslv = dslv; - mDetector = new GestureDetector(dslv.getContext(), this); - mFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener); - mFlingRemoveDetector.setIsLongpressEnabled(false); - mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop(); - mDragHandleId = dragHandleId; - mClickRemoveId = clickRemoveId; - mFlingHandleId = flingHandleId; - setRemoveMode(removeMode); - setDragInitMode(dragInitMode); - } - - public int getDragInitMode() { - return mDragInitMode; - } - - /** - * Set how a drag is initiated. Needs to be one of {@link ON_DOWN}, - * {@link ON_DRAG}, or {@link ON_LONG_PRESS}. - * - * @param mode - * The drag init mode. - */ - public void setDragInitMode(int mode) { - mDragInitMode = mode; - } - - /** - * Enable/Disable list item sorting. Disabling is useful if only item - * removal is desired. Prevents drags in the vertical direction. - * - * @param enabled - * Set true to enable list item sorting. - */ - public void setSortEnabled(boolean enabled) { - mSortEnabled = enabled; - } - - public boolean isSortEnabled() { - return mSortEnabled; - } - - /** - * One of {@link CLICK_REMOVE}, {@link FLING_RIGHT_REMOVE}, - * {@link FLING_LEFT_REMOVE}, {@link SLIDE_RIGHT_REMOVE}, or - * {@link SLIDE_LEFT_REMOVE}. - */ - public void setRemoveMode(int mode) { - mRemoveMode = mode; - } - - public int getRemoveMode() { - return mRemoveMode; - } - - /** - * Enable/Disable item removal without affecting remove mode. - */ - public void setRemoveEnabled(boolean enabled) { - mRemoveEnabled = enabled; - } - - public boolean isRemoveEnabled() { - return mRemoveEnabled; - } - - /** - * Set the resource id for the View that represents the drag handle in a - * list item. - * - * @param id - * An android resource id. - */ - public void setDragHandleId(int id) { - mDragHandleId = id; - } - - /** - * Set the resource id for the View that represents the fling handle in a - * list item. - * - * @param id - * An android resource id. - */ - public void setFlingHandleId(int id) { - mFlingHandleId = id; - } - - /** - * Set the resource id for the View that represents click removal button. - * - * @param id - * An android resource id. - */ - public void setClickRemoveId(int id) { - mClickRemoveId = id; - } - - /** - * Sets flags to restrict certain motions of the floating View based on - * DragSortController settings (such as remove mode). Starts the drag on the - * DragSortListView. - * - * @param position - * The list item position (includes headers). - * @param deltaX - * Touch x-coord minus left edge of floating View. - * @param deltaY - * Touch y-coord minus top edge of floating View. - * - * @return True if drag started, false otherwise. - */ - public boolean startDrag(int position, int deltaX, int deltaY) { - - int dragFlags = 0; - if (mSortEnabled && !mIsRemoving) { - dragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y; - } - if (mRemoveEnabled && mIsRemoving) { - dragFlags |= DragSortListView.DRAG_POS_X; - dragFlags |= DragSortListView.DRAG_NEG_X; - } - - mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX, deltaY); - return mDragging; - } - - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (!mDslv.isDragEnabled() || mDslv.listViewIntercepted()) { - return false; - } - - mDetector.onTouchEvent(ev); - if (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) { - mFlingRemoveDetector.onTouchEvent(ev); - } - - int action = ev.getAction() & MotionEvent.ACTION_MASK; - switch (action) { - case MotionEvent.ACTION_DOWN: - mCurrX = (int) ev.getX(); - mCurrY = (int) ev.getY(); - break; - case MotionEvent.ACTION_UP: - if (mRemoveEnabled && mIsRemoving) { - int x = mPositionX >= 0 ? mPositionX : -mPositionX; - int removePoint = mDslv.getWidth() / 2; - if (x > removePoint) { - mDslv.stopDragWithVelocity(true, 0); - } - } - case MotionEvent.ACTION_CANCEL: - mIsRemoving = false; - mDragging = false; - break; - } - - return false; - } - - /** - * Overrides to provide fading when slide removal is enabled. - */ - @Override - public void onDragFloatView(View floatView, Point position, Point touch) { - - if (mRemoveEnabled && mIsRemoving) { - mPositionX = position.x; - } - } - - /** - * Get the position to start dragging based on the ACTION_DOWN MotionEvent. - * This function simply calls {@link #dragHandleHitPosition(MotionEvent)}. - * Override to change drag handle behavior; this function is called - * internally when an ACTION_DOWN event is detected. - * - * @param ev - * The ACTION_DOWN MotionEvent. - * - * @return The list position to drag if a drag-init gesture is detected; - * MISS if unsuccessful. - */ - public int startDragPosition(MotionEvent ev) { - return dragHandleHitPosition(ev); - } - - public int startFlingPosition(MotionEvent ev) { - return mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS; - } - - /** - * Checks for the touch of an item's drag handle (specified by - * {@link #setDragHandleId(int)}), and returns that item's position if a - * drag handle touch was detected. - * - * @param ev - * The ACTION_DOWN MotionEvent. - * - * @return The list position of the item whose drag handle was touched; MISS - * if unsuccessful. - */ - public int dragHandleHitPosition(MotionEvent ev) { - return viewIdHitPosition(ev, mDragHandleId); - } - - public int flingHandleHitPosition(MotionEvent ev) { - return viewIdHitPosition(ev, mFlingHandleId); - } - - public int viewIdHitPosition(MotionEvent ev, int id) { - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); - - int touchPos = mDslv.pointToPosition(x, y); // includes headers/footers - - final int numHeaders = mDslv.getHeaderViewsCount(); - final int numFooters = mDslv.getFooterViewsCount(); - final int count = mDslv.getCount(); - - // Log.d("mobeta", "touch down on position " + itemnum); - // We're only interested if the touch was on an - // item that's not a header or footer. - if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders && touchPos < (count - numFooters)) { - final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition()); - final int rawX = (int) ev.getRawX(); - final int rawY = (int) ev.getRawY(); - - View dragBox = id == 0 ? item : (View) item.findViewById(id); - if (dragBox != null) { - dragBox.getLocationOnScreen(mTempLoc); - - if (rawX > mTempLoc[0] && rawY > mTempLoc[1] && rawX < mTempLoc[0] + dragBox.getWidth() - && rawY < mTempLoc[1] + dragBox.getHeight()) { - - mItemX = item.getLeft(); - mItemY = item.getTop(); - - return touchPos; - } - } - } - - return MISS; - } - - @Override - public boolean onDown(MotionEvent ev) { - if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) { - mClickRemoveHitPos = viewIdHitPosition(ev, mClickRemoveId); - } - - mHitPos = startDragPosition(ev); - if (mHitPos != MISS && mDragInitMode == ON_DOWN) { - startDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY); - } - - mIsRemoving = false; - mCanDrag = true; - mPositionX = 0; - mFlingHitPos = startFlingPosition(ev); - - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - - final int x1 = (int) e1.getX(); - final int y1 = (int) e1.getY(); - final int x2 = (int) e2.getX(); - final int y2 = (int) e2.getY(); - final int deltaX = x2 - mItemX; - final int deltaY = y2 - mItemY; - - if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) { - if (mHitPos != MISS) { - if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) { - startDrag(mHitPos, deltaX, deltaY); - } else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) { - mIsRemoving = true; - startDrag(mFlingHitPos, deltaX, deltaY); - } - } else if (mFlingHitPos != MISS) { - if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) { - mIsRemoving = true; - startDrag(mFlingHitPos, deltaX, deltaY); - } else if (Math.abs(y2 - y1) > mTouchSlop) { - mCanDrag = false; // if started to scroll the list then - // don't allow sorting nor fling-removing - } - } - } - // return whatever - return false; - } - - @Override - public void onLongPress(MotionEvent e) { - // Log.d("mobeta", "lift listener long pressed"); - if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) { - mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY); - } - } - - // complete the OnGestureListener interface - @Override - public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - return false; - } - - // complete the OnGestureListener interface - @Override - public boolean onSingleTapUp(MotionEvent ev) { - if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) { - if (mClickRemoveHitPos != MISS) { - mDslv.removeItem(mClickRemoveHitPos - mDslv.getHeaderViewsCount()); - } - } - return true; - } - - // complete the OnGestureListener interface - @Override - public void onShowPress(MotionEvent ev) { - // do nothing - } - - private GestureDetector.OnGestureListener mFlingRemoveListener = new GestureDetector.SimpleOnGestureListener() { - @Override - public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - // Log.d("mobeta", "on fling remove called"); - if (mRemoveEnabled && mIsRemoving) { - int w = mDslv.getWidth(); - int minPos = w / 5; - if (velocityX > mFlingSpeed) { - if (mPositionX > -minPos) { - mDslv.stopDragWithVelocity(true, velocityX); - } - } else if (velocityX < -mFlingSpeed) { - if (mPositionX < minPos) { - mDslv.stopDragWithVelocity(true, velocityX); - } - } - mIsRemoving = false; - } - return false; - } - }; - -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortCursorAdapter.java b/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortCursorAdapter.java deleted file mode 100644 index 0ba1e299..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortCursorAdapter.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.mobeta.android.dslv; - -import java.util.ArrayList; - -import android.content.Context; -import android.database.Cursor; -import android.support.v4.widget.CursorAdapter; -import android.util.SparseIntArray; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListAdapter; - -/** - * A subclass of {@link android.widget.CursorAdapter} that provides reordering - * of the elements in the Cursor based on completed drag-sort operations. The - * reordering is a simple mapping of list positions into Cursor positions (the - * Cursor is unchanged). To persist changes made by drag-sorts, one can retrieve - * the mapping with the {@link #getCursorPositions()} method, which returns the - * reordered list of Cursor positions. - * - * An instance of this class is passed to - * {@link DragSortListView#setAdapter(ListAdapter)} and, since this class - * implements the {@link DragSortListView.DragSortListener} interface, it is - * automatically set as the DragSortListener for the DragSortListView instance. - */ -public abstract class DragSortCursorAdapter extends CursorAdapter implements DragSortListView.DragSortListener { - - public static final int REMOVED = -1; - - /** - * Key is ListView position, value is Cursor position - */ - private final SparseIntArray mListMapping = new SparseIntArray(); - - private final ArrayList mRemovedCursorPositions = new ArrayList(); - - @SuppressWarnings("deprecation") - public DragSortCursorAdapter(Context context, Cursor c) { - super(context, c); - } - - public DragSortCursorAdapter(Context context, Cursor c, boolean autoRequery) { - super(context, c, autoRequery); - } - - public DragSortCursorAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - } - - /** - * Swaps Cursor and clears list-Cursor mapping. - * - * @see android.widget.CursorAdapter#swapCursor(android.database.Cursor) - */ - @Override - public Cursor swapCursor(Cursor newCursor) { - Cursor old = super.swapCursor(newCursor); - resetMappings(); - return old; - } - - /** - * Changes Cursor and clears list-Cursor mapping. - * - * @see android.widget.CursorAdapter#changeCursor(android.database.Cursor) - */ - @Override - public void changeCursor(Cursor cursor) { - super.changeCursor(cursor); - resetMappings(); - } - - /** - * Resets list-cursor mapping. - */ - public void reset() { - resetMappings(); - notifyDataSetChanged(); - } - - private void resetMappings() { - mListMapping.clear(); - mRemovedCursorPositions.clear(); - } - - @Override - public Object getItem(int position) { - return super.getItem(mListMapping.get(position, position)); - } - - @Override - public long getItemId(int position) { - return super.getItemId(mListMapping.get(position, position)); - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - return super.getDropDownView(mListMapping.get(position, position), convertView, parent); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return super.getView(mListMapping.get(position, position), convertView, parent); - } - - /** - * On drop, this updates the mapping between Cursor positions and ListView - * positions. The Cursor is unchanged. Retrieve the current mapping with - * {@link getCursorPositions()}. - * - * @see DragSortListView.DropListener#drop(int, int) - */ - @Override - public void drop(int from, int to) { - if (from != to) { - int cursorFrom = mListMapping.get(from, from); - - if (from > to) { - for (int i = from; i > to; --i) { - mListMapping.put(i, mListMapping.get(i - 1, i - 1)); - } - } else { - for (int i = from; i < to; ++i) { - mListMapping.put(i, mListMapping.get(i + 1, i + 1)); - } - } - mListMapping.put(to, cursorFrom); - - cleanMapping(); - notifyDataSetChanged(); - } - } - - /** - * On remove, this updates the mapping between Cursor positions and ListView - * positions. The Cursor is unchanged. Retrieve the current mapping with - * {@link getCursorPositions()}. - * - * @see DragSortListView.RemoveListener#remove(int) - */ - @Override - public void remove(int which) { - int cursorPos = mListMapping.get(which, which); - if (!mRemovedCursorPositions.contains(cursorPos)) { - mRemovedCursorPositions.add(cursorPos); - } - - int newCount = getCount(); - for (int i = which; i < newCount; ++i) { - mListMapping.put(i, mListMapping.get(i + 1, i + 1)); - } - - mListMapping.delete(newCount); - - cleanMapping(); - notifyDataSetChanged(); - } - - /** - * Does nothing. Just completes DragSortListener interface. - */ - @Override - public void drag(int from, int to) { - // do nothing - } - - /** - * Remove unnecessary mappings from sparse array. - */ - private void cleanMapping() { - ArrayList toRemove = new ArrayList(); - - int size = mListMapping.size(); - for (int i = 0; i < size; ++i) { - if (mListMapping.keyAt(i) == mListMapping.valueAt(i)) { - toRemove.add(mListMapping.keyAt(i)); - } - } - - size = toRemove.size(); - for (int i = 0; i < size; ++i) { - mListMapping.delete(toRemove.get(i)); - } - } - - @Override - public int getCount() { - return super.getCount() - mRemovedCursorPositions.size(); - } - - /** - * Get the Cursor position mapped to by the provided list position (given - * all previously handled drag-sort operations). - * - * @param position - * List position - * - * @return The mapped-to Cursor position - */ - public int getCursorPosition(int position) { - return mListMapping.get(position, position); - } - - /** - * Get the current order of Cursor positions presented by the list. - */ - public ArrayList getCursorPositions() { - ArrayList result = new ArrayList(); - - for (int i = 0; i < getCount(); ++i) { - result.add(mListMapping.get(i, i)); - } - - return result; - } - - /** - * Get the list position mapped to by the provided Cursor position. If the - * provided Cursor position has been removed by a drag-sort, this returns - * {@link #REMOVED}. - * - * @param cursorPosition - * A Cursor position - * @return The mapped-to list position or REMOVED - */ - public int getListPosition(int cursorPosition) { - if (mRemovedCursorPositions.contains(cursorPosition)) { - return REMOVED; - } - - int index = mListMapping.indexOfValue(cursorPosition); - if (index < 0) { - return cursorPosition; - } else { - return mListMapping.keyAt(index); - } - } - -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemView.java b/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemView.java deleted file mode 100644 index c6d5eeb4..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemView.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.mobeta.android.dslv; - -import android.content.Context; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; - -/** - * Lightweight ViewGroup that wraps list items obtained from user's ListAdapter. - * ItemView expects a single child that has a definite height (i.e. the child's - * layout height is not MATCH_PARENT). The width of ItemView will always match - * the width of its child (that is, the width MeasureSpec given to ItemView is - * passed directly to the child, and the ItemView measured width is set to the - * child's measured width). The height of ItemView can be anything; the - * - * - * The purpose of this class is to optimize slide shuffle animations. - */ -public class DragSortItemView extends ViewGroup { - - private int mGravity = Gravity.TOP; - - public DragSortItemView(Context context) { - super(context); - - // always init with standard ListView layout params - setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - //setClipChildren(true); - } - - public void setGravity(int gravity) { - mGravity = gravity; - } - - public int getGravity() { - return mGravity; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - final View child = getChildAt(0); - - if (child == null) { - return; - } - - if (mGravity == Gravity.TOP) { - child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight()); - } else { - child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight()); - } - } - - /** - * - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - - int height = MeasureSpec.getSize(heightMeasureSpec); - int width = MeasureSpec.getSize(widthMeasureSpec); - - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - - final View child = getChildAt(0); - if (child == null) { - setMeasuredDimension(0, width); - return; - } - - if (child.isLayoutRequested()) { - // Always let child be as tall as it wants. - measureChild(child, widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - } - - if (heightMode == MeasureSpec.UNSPECIFIED) { - ViewGroup.LayoutParams lp = getLayoutParams(); - - if (lp.height > 0) { - height = lp.height; - } else { - height = child.getMeasuredHeight(); - } - } - - setMeasuredDimension(width, height); - } - -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemViewCheckable.java b/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemViewCheckable.java deleted file mode 100644 index a5a81235..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortItemViewCheckable.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mobeta.android.dslv; - -import android.content.Context; -import android.view.View; -import android.widget.Checkable; - -/** - * Lightweight ViewGroup that wraps list items obtained from user's ListAdapter. - * ItemView expects a single child that has a definite height (i.e. the child's - * layout height is not MATCH_PARENT). The width of ItemView will always match - * the width of its child (that is, the width MeasureSpec given to ItemView is - * passed directly to the child, and the ItemView measured width is set to the - * child's measured width). The height of ItemView can be anything; the - * - * - * The purpose of this class is to optimize slide shuffle animations. - */ -public class DragSortItemViewCheckable extends DragSortItemView implements Checkable { - - public DragSortItemViewCheckable(Context context) { - super(context); - } - - @Override - public boolean isChecked() { - View child = getChildAt(0); - if (child instanceof Checkable) - return ((Checkable) child).isChecked(); - else - return false; - } - - @Override - public void setChecked(boolean checked) { - View child = getChildAt(0); - if (child instanceof Checkable) - ((Checkable) child).setChecked(checked); - } - - @Override - public void toggle() { - View child = getChildAt(0); - if (child instanceof Checkable) - ((Checkable) child).toggle(); - } -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortListView.java b/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortListView.java deleted file mode 100644 index ca2e618e..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/DragSortListView.java +++ /dev/null @@ -1,2974 +0,0 @@ -/* - * DragSortListView. - * - * A subclass of the Android ListView component that enables drag - * and drop re-ordering of list items. - * - * Copyright 2012 Carl Bauer - * - * 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 com.mobeta.android.dslv; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; - -import android.content.Context; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.os.Environment; -import android.os.SystemClock; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseBooleanArray; -import android.util.SparseIntArray; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.BaseAdapter; -import android.widget.Checkable; -import android.widget.ListAdapter; -import android.widget.ListView; - -/** - * ListView subclass that mediates drag and drop resorting of items. - * - * - * @author heycosmo - * - */ -@SuppressWarnings("unused") -public class DragSortListView extends ListView { - - /** - * The View that floats above the ListView and represents the dragged item. - */ - private View mFloatView; - - /** - * The float View location. First based on touch location and given deltaX - * and deltaY. Then restricted by callback to - * FloatViewManager.onDragFloatView(). Finally restricted by bounds of DSLV. - */ - private final Point mFloatLoc = new Point(); - - private final Point mTouchLoc = new Point(); - - /** - * The middle (in the y-direction) of the floating View. - */ - private int mFloatViewMid; - - /** - * Flag to make sure float View isn't measured twice - */ - private boolean mFloatViewOnMeasured = false; - - /** - * Watch the Adapter for data changes. Cancel a drag if coincident with a - * change. - */ - private final DataSetObserver mObserver; - - /** - * Transparency for the floating View (XML attribute). - */ - private final float mFloatAlpha = 1.0f; - private float mCurrFloatAlpha = 1.0f; - - /** - * While drag-sorting, the current position of the floating View. If - * dropped, the dragged item will land in this position. - */ - private int mFloatPos; - - /** - * The first expanded ListView position that helps represent the drop slot - * tracking the floating View. - */ - private int mFirstExpPos; - - /** - * The second expanded ListView position that helps represent the drop slot - * tracking the floating View. This can equal mFirstExpPos if there is no - * slide shuffle occurring; otherwise it is equal to mFirstExpPos + 1. - */ - private int mSecondExpPos; - - /** - * Flag set if slide shuffling is enabled. - */ - private final boolean mAnimate = false; - - /** - * The user dragged from this position. - */ - private int mSrcPos; - - /** - * Offset (in x) within the dragged item at which the user picked it up (or - * first touched down with the digitalis). - */ - private int mDragDeltaX; - - /** - * Offset (in y) within the dragged item at which the user picked it up (or - * first touched down with the digitalis). - */ - private int mDragDeltaY; - - /** - * The difference (in x) between screen coordinates and coordinates in this - * view. - */ - private int mOffsetX; - - /** - * The difference (in y) between screen coordinates and coordinates in this - * view. - */ - private int mOffsetY; - - /** - * A listener that receives callbacks whenever the floating View hovers over - * a new position. - */ - private DragListener mDragListener; - - /** - * A listener that receives a callback when the floating View is dropped. - */ - private DropListener mDropListener; - - /** - * A listener that receives a callback when the floating View (or more - * precisely the originally dragged item) is removed by one of the provided - * gestures. - */ - private RemoveListener mRemoveListener; - - /** - * Enable/Disable item dragging - * - * @attr name dslv:drag_enabled - */ - private boolean mDragEnabled = true; - - /** - * Drag state enum. - */ - private final static int IDLE = 0; - private final static int REMOVING = 1; - private final static int DROPPING = 2; - private final static int STOPPED = 3; - private final static int DRAGGING = 4; - - private int mDragState = IDLE; - - /** - * Height in pixels to which the originally dragged item is collapsed during - * a drag-sort. Currently, this value must be greater than zero. - */ - private final int mItemHeightCollapsed = 1; - - /** - * Height of the floating View. Stored for the purpose of providing the - * tracking drop slot. - */ - private int mFloatViewHeight; - - /** - * Convenience member. See above. - */ - private int mFloatViewHeightHalf; - - /** - * Save the given width spec for use in measuring children - */ - private int mWidthMeasureSpec = 0; - - /** - * Sample Views ultimately used for calculating the height of ListView items - * that are off-screen. - */ - private View[] mSampleViewTypes = new View[1]; - - /** - * Drag-scroll encapsulator! - */ - private final DragScroller mDragScroller; - - /** - * Determines the start of the upward drag-scroll region at the top of the - * ListView. Specified by a fraction of the ListView height, thus screen - * resolution agnostic. - */ - private float mDragUpScrollStartFrac = 1.0f / 3.0f; - - /** - * Determines the start of the downward drag-scroll region at the bottom of - * the ListView. Specified by a fraction of the ListView height, thus screen - * resolution agnostic. - */ - private float mDragDownScrollStartFrac = 1.0f / 3.0f; - - /** - * The following are calculated from the above fracs. - */ - private int mUpScrollStartY; - private int mDownScrollStartY; - private float mDownScrollStartYF; - private float mUpScrollStartYF; - - /** - * Calculated from above above and current ListView height. - */ - private float mDragUpScrollHeight; - - /** - * Calculated from above above and current ListView height. - */ - private float mDragDownScrollHeight; - - /** - * Maximum drag-scroll speed in pixels per ms. Only used with default linear - * drag-scroll profile. - */ - private float mMaxScrollSpeed = 0.5f; - - /** - * Defines the scroll speed during a drag-scroll. User can provide their - * own; this default is a simple linear profile where scroll speed increases - * linearly as the floating View nears the top/bottom of the ListView. - */ - private DragScrollProfile mScrollProfile = new DragScrollProfile() { - @Override - public float getSpeed(float w, long t) { - return mMaxScrollSpeed * w; - } - }; - - /** - * Current touch x. - */ - private int mX; - - /** - * Current touch y. - */ - private int mY; - - /** - * Last touch x. - */ - private int mLastX; - - /** - * Last touch y. - */ - private int mLastY; - - /** - * The touch y-coord at which drag started - */ - private int mDragStartY; - - /** - * Drag flag bit. Floating View can move in the positive x direction. - */ - public final static int DRAG_POS_X = 0x1; - - /** - * Drag flag bit. Floating View can move in the negative x direction. - */ - public final static int DRAG_NEG_X = 0x2; - - /** - * Drag flag bit. Floating View can move in the positive y direction. This - * is subtle. What this actually means is that, if enabled, the floating - * View can be dragged below its starting position. Remove in favor of - * upper-bounding item position? - */ - public final static int DRAG_POS_Y = 0x4; - - /** - * Drag flag bit. Floating View can move in the negative y direction. This - * is subtle. What this actually means is that the floating View can be - * dragged above its starting position. Remove in favor of lower-bounding - * item position? - */ - public final static int DRAG_NEG_Y = 0x8; - - /** - * Flags that determine limits on the motion of the floating View. See flags - * above. - */ - private int mDragFlags = 0; - - /** - * Last call to an on*TouchEvent was a call to onInterceptTouchEvent. - */ - private boolean mLastCallWasIntercept = false; - - /** - * A touch event is in progress. - */ - private boolean mInTouchEvent = false; - - /** - * Let the user customize the floating View. - */ - private FloatViewManager mFloatViewManager = null; - - /** - * Given to ListView to cancel its action when a drag-sort begins. - */ - private final MotionEvent mCancelEvent; - - /** - * Enum telling where to cancel the ListView action when a drag-sort begins - */ - private static final int NO_CANCEL = 0; - private static final int ON_TOUCH_EVENT = 1; - private static final int ON_INTERCEPT_TOUCH_EVENT = 2; - - /** - * Where to cancel the ListView action when a drag-sort begins - */ - private int mCancelMethod = NO_CANCEL; - - /** - * Determines when a slide shuffle animation starts. That is, defines how - * close to the edge of the drop slot the floating View must be to initiate - * the slide. - */ - private final float mSlideRegionFrac = 0.25f; - - /** - * Number between 0 and 1 indicating the relative location of a sliding item - * (only used if drag-sort animations are turned on). Nearly 1 means the - * item is at the top of the slide region (nearly full blank item is - * directly below). - */ - private float mSlideFrac = 0.0f; - - /** - * Wraps the user-provided ListAdapter. This is used to wrap each item View - * given by the user inside another View (currenly a RelativeLayout) which - * expands and collapses to simulate the item shuffling. - */ - private AdapterWrapper mAdapterWrapper; - - /** - * Turn on custom debugger. - */ - private final boolean mTrackDragSort = false; - - /** - * Debugging class. - */ - private DragSortTracker mDragSortTracker; - - /** - * Needed for adjusting item heights from within layoutChildren - */ - private boolean mBlockLayoutRequests = false; - - /** - * Set to true when a down event happens during drag sort; for example, when - * drag finish animations are playing. - */ - private boolean mIgnoreTouchEvent = false; - - /** - * Caches DragSortItemView child heights. Sometimes DSLV has to know the - * height of an offscreen item. Since ListView virtualizes these, DSLV must - * get the item from the ListAdapter to obtain its height. That process can - * be expensive, but often the same offscreen item will be requested many - * times in a row. Once an offscreen item height is calculated, we cache it - * in this guy. Actually, we cache the height of the child of the - * DragSortItemView since the item height changes often during a drag-sort. - */ - private static final int sCacheSize = 3; - private final HeightCache mChildHeightCache = new HeightCache(sCacheSize); - - private RemoveAnimator mRemoveAnimator; - - private LiftAnimator mLiftAnimator; - - private DropAnimator mDropAnimator; - - private boolean mUseRemoveVelocity; - private float mRemoveVelocityX = 0; - - public DragSortListView(Context context, AttributeSet attrs) { - super(context, attrs); - - int defaultDuration = 150; - int removeAnimDuration = defaultDuration; // ms - int dropAnimDuration = defaultDuration; // ms - - mDragScroller = new DragScroller(); - - float smoothness = 0.5f; - if (removeAnimDuration > 0) { - mRemoveAnimator = new RemoveAnimator(smoothness, removeAnimDuration); - } - // mLiftAnimator = new LiftAnimator(smoothness, 100); - if (dropAnimDuration > 0) { - mDropAnimator = new DropAnimator(smoothness, dropAnimDuration); - } - - mCancelEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0f, 0f, 0, 0f, 0f, 0, 0); - - // construct the dataset observer - mObserver = new DataSetObserver() { - private void cancel() { - if (mDragState == DRAGGING) { - cancelDrag(); - } - } - - @Override - public void onChanged() { - cancel(); - } - - @Override - public void onInvalidated() { - cancel(); - } - }; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mCancelEvent.recycle(); - } - - public boolean isDragging() { - return mDragState == DRAGGING; - } - - /** - * Usually called from a FloatViewManager. The float alpha will be reset to - * the xml-defined value every time a drag is stopped. - */ - public void setFloatAlpha(float alpha) { - mCurrFloatAlpha = alpha; - } - - public float getFloatAlpha() { - return mCurrFloatAlpha; - } - - /** - * Set maximum drag scroll speed in positions/second. Only applies if using - * default ScrollSpeedProfile. - * - * @param max - * Maximum scroll speed. - */ - public void setMaxScrollSpeed(float max) { - mMaxScrollSpeed = max; - } - - /** - * For each DragSortListView Listener interface implemented by - * adapter, this method calls the appropriate set*Listener - * method with adapter as the argument. - * - * @param adapter - * The ListAdapter providing data to back DragSortListView. - * - * @see android.widget.ListView#setAdapter(android.widget.ListAdapter) - */ - @Override - public void setAdapter(ListAdapter adapter) { - if (adapter != null) { - mAdapterWrapper = new AdapterWrapper(adapter); - adapter.registerDataSetObserver(mObserver); - - if (adapter instanceof DropListener) { - setDropListener((DropListener) adapter); - } - if (adapter instanceof DragListener) { - setDragListener((DragListener) adapter); - } - if (adapter instanceof RemoveListener) { - setRemoveListener((RemoveListener) adapter); - } - } else { - mAdapterWrapper = null; - } - - super.setAdapter(mAdapterWrapper); - } - - /** - * As opposed to {@link ListView#getAdapter()}, which returns a heavily - * wrapped ListAdapter (DragSortListView wraps the input ListAdapter {\emph - * and} ListView wraps the wrapped one). - * - * @return The ListAdapter set as the argument of {@link setAdapter()} - */ - public ListAdapter getInputAdapter() { - if (mAdapterWrapper == null) { - return null; - } else { - return mAdapterWrapper.getAdapter(); - } - } - - private class AdapterWrapper extends BaseAdapter { - private final ListAdapter mAdapter; - - public AdapterWrapper(ListAdapter adapter) { - super(); - mAdapter = adapter; - - mAdapter.registerDataSetObserver(new DataSetObserver() { - @Override - public void onChanged() { - notifyDataSetChanged(); - } - - @Override - public void onInvalidated() { - notifyDataSetInvalidated(); - } - }); - } - - public ListAdapter getAdapter() { - return mAdapter; - } - - @Override - public long getItemId(int position) { - return mAdapter.getItemId(position); - } - - @Override - public Object getItem(int position) { - return mAdapter.getItem(position); - } - - @Override - public int getCount() { - return mAdapter.getCount(); - } - - @Override - public boolean areAllItemsEnabled() { - return mAdapter.areAllItemsEnabled(); - } - - @Override - public boolean isEnabled(int position) { - return mAdapter.isEnabled(position); - } - - @Override - public int getItemViewType(int position) { - return mAdapter.getItemViewType(position); - } - - @Override - public int getViewTypeCount() { - return mAdapter.getViewTypeCount(); - } - - @Override - public boolean hasStableIds() { - return mAdapter.hasStableIds(); - } - - @Override - public boolean isEmpty() { - return mAdapter.isEmpty(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - DragSortItemView v; - View child; - // Log.d("mobeta", - // "getView: position="+position+" convertView="+convertView); - if (convertView != null) { - v = (DragSortItemView) convertView; - View oldChild = v.getChildAt(0); - - child = mAdapter.getView(position, oldChild, DragSortListView.this); - if (child != oldChild) { - // shouldn't get here if user is reusing convertViews - // properly - if (oldChild != null) { - v.removeViewAt(0); - } - v.addView(child); - } - } else { - child = mAdapter.getView(position, null, DragSortListView.this); - if (child instanceof Checkable) { - v = new DragSortItemViewCheckable(getContext()); - } else { - v = new DragSortItemView(getContext()); - } - v.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - v.addView(child); - } - - // Set the correct item height given drag state; passed - // View needs to be measured if measurement is required. - adjustItem(position + getHeaderViewsCount(), v, true); - - return v; - } - } - - private void drawDivider(int expPosition, Canvas canvas) { - - final Drawable divider = getDivider(); - final int dividerHeight = getDividerHeight(); - // Log.d("mobeta", "div="+divider+" divH="+dividerHeight); - - if (divider != null && dividerHeight != 0) { - final ViewGroup expItem = (ViewGroup) getChildAt(expPosition - getFirstVisiblePosition()); - if (expItem != null) { - final int l = getPaddingLeft(); - final int r = getWidth() - getPaddingRight(); - final int t; - final int b; - - final int childHeight = expItem.getChildAt(0).getHeight(); - - if (expPosition > mSrcPos) { - t = expItem.getTop() + childHeight; - b = t + dividerHeight; - } else { - b = expItem.getBottom() - childHeight; - t = b - dividerHeight; - } - // Log.d("mobeta", "l="+l+" t="+t+" r="+r+" b="+b); - - // Have to clip to support ColorDrawable on <= Gingerbread - canvas.save(); - canvas.clipRect(l, t, r, b); - divider.setBounds(l, t, r, b); - divider.draw(canvas); - canvas.restore(); - } - } - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - - if (mDragState != IDLE) { - // draw the divider over the expanded item - if (mFirstExpPos != mSrcPos) { - drawDivider(mFirstExpPos, canvas); - } - if (mSecondExpPos != mFirstExpPos && mSecondExpPos != mSrcPos) { - drawDivider(mSecondExpPos, canvas); - } - } - - if (mFloatView != null) { - // draw the float view over everything - final int w = mFloatView.getWidth(); - final int h = mFloatView.getHeight(); - - int x = mFloatLoc.x; - - int width = getWidth(); - if (x < 0) - x = -x; - float alphaMod; - if (x < width) { - alphaMod = ((float) (width - x)) / ((float) width); - alphaMod *= alphaMod; - } else { - alphaMod = 0; - } - - final int alpha = (int) (255f * mCurrFloatAlpha * alphaMod); - - canvas.save(); - // Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); - canvas.translate(mFloatLoc.x, mFloatLoc.y); - canvas.clipRect(0, 0, w, h); - - // Log.d("mobeta", "clip rect bounds: " + canvas.getClipBounds()); - canvas.saveLayerAlpha(0, 0, w, h, alpha, Canvas.ALL_SAVE_FLAG); - mFloatView.draw(canvas); - canvas.restore(); - canvas.restore(); - } - } - - private int getItemHeight(int position) { - View v = getChildAt(position - getFirstVisiblePosition()); - - if (v != null) { - // item is onscreen, just get the height of the View - return v.getHeight(); - } else { - // item is offscreen. get child height and calculate - // item height based on current shuffle state - return calcItemHeight(position, getChildHeight(position)); - } - } - - private void printPosData() { - Log.d("mobeta", "mSrcPos=" + mSrcPos + " mFirstExpPos=" + mFirstExpPos + " mSecondExpPos=" + mSecondExpPos); - } - - private class HeightCache { - - private final SparseIntArray mMap; - private final ArrayList mOrder; - private final int mMaxSize; - - public HeightCache(int size) { - mMap = new SparseIntArray(size); - mOrder = new ArrayList(size); - mMaxSize = size; - } - - /** - * Add item height at position if doesn't already exist. - */ - public void add(int position, int height) { - int currHeight = mMap.get(position, -1); - if (currHeight != height) { - if (currHeight == -1) { - if (mMap.size() == mMaxSize) { - // remove oldest entry - mMap.delete(mOrder.remove(0)); - } - } else { - // move position to newest slot - mOrder.remove((Integer) position); - } - mMap.put(position, height); - mOrder.add(position); - } - } - - public int get(int position) { - return mMap.get(position, -1); - } - - public void clear() { - mMap.clear(); - mOrder.clear(); - } - - } - - /** - * Get the shuffle edge for item at position when top of item is at y-coord - * top. Assumes that current item heights are consistent with current float - * view location and thus expanded positions and slide fraction. i.e. Should - * not be called between update of expanded positions/slide fraction and - * layoutChildren. - * - * @param position - * @param top - * @param height - * Height of item at position. If -1, this function calculates - * this height. - * - * @return Shuffle line between position-1 and position (for the given view - * of the list; that is, for when top of item at position has - * y-coord of given `top`). If floating View (treated as horizontal - * line) is dropped immediately above this line, it lands in - * position-1. If dropped immediately below this line, it lands in - * position. - */ - private int getShuffleEdge(int position, int top) { - - final int numHeaders = getHeaderViewsCount(); - final int numFooters = getFooterViewsCount(); - - // shuffle edges are defined between items that can be - // dragged; there are N-1 of them if there are N draggable - // items. - - if (position <= numHeaders || (position >= getCount() - numFooters)) { - return top; - } - - int divHeight = getDividerHeight(); - - int edge; - - int maxBlankHeight = mFloatViewHeight - mItemHeightCollapsed; - int childHeight = getChildHeight(position); - int itemHeight = getItemHeight(position); - - // first calculate top of item given that floating View is - // centered over src position - int otop = top; - if (mSecondExpPos <= mSrcPos) { - // items are expanded on and/or above the source position - - if (position == mSecondExpPos && mFirstExpPos != mSecondExpPos) { - if (position == mSrcPos) { - otop = top + itemHeight - mFloatViewHeight; - } else { - int blankHeight = itemHeight - childHeight; - otop = top + blankHeight - maxBlankHeight; - } - } else if (position > mSecondExpPos && position <= mSrcPos) { - otop = top - maxBlankHeight; - } - - } else { - // items are expanded on and/or below the source position - - if (position > mSrcPos && position <= mFirstExpPos) { - otop = top + maxBlankHeight; - } else if (position == mSecondExpPos && mFirstExpPos != mSecondExpPos) { - int blankHeight = itemHeight - childHeight; - otop = top + blankHeight; - } - } - - // otop is set - if (position <= mSrcPos) { - edge = otop + (mFloatViewHeight - divHeight - getChildHeight(position - 1)) / 2; - } else { - edge = otop + (childHeight - divHeight - mFloatViewHeight) / 2; - } - - return edge; - } - - private boolean updatePositions() { - - final int first = getFirstVisiblePosition(); - int startPos = mFirstExpPos; - View startView = getChildAt(startPos - first); - - if (startView == null) { - startPos = first + getChildCount() / 2; - startView = getChildAt(startPos - first); - } - int startTop = startView.getTop(); - - int itemHeight = startView.getHeight(); - - int edge = getShuffleEdge(startPos, startTop); - int lastEdge = edge; - - int divHeight = getDividerHeight(); - - // Log.d("mobeta", "float mid="+mFloatViewMid); - - int itemPos = startPos; - int itemTop = startTop; - if (mFloatViewMid < edge) { - // scanning up for float position - // Log.d("mobeta", " edge="+edge); - while (itemPos >= 0) { - itemPos--; - itemHeight = getItemHeight(itemPos); - - if (itemPos == 0) { - edge = itemTop - divHeight - itemHeight; - break; - } - - itemTop -= itemHeight + divHeight; - edge = getShuffleEdge(itemPos, itemTop); - // Log.d("mobeta", " edge="+edge); - - if (mFloatViewMid >= edge) { - break; - } - - lastEdge = edge; - } - } else { - // scanning down for float position - // Log.d("mobeta", " edge="+edge); - final int count = getCount(); - while (itemPos < count) { - if (itemPos == count - 1) { - edge = itemTop + divHeight + itemHeight; - break; - } - - itemTop += divHeight + itemHeight; - itemHeight = getItemHeight(itemPos + 1); - edge = getShuffleEdge(itemPos + 1, itemTop); - // Log.d("mobeta", " edge="+edge); - - // test for hit - if (mFloatViewMid < edge) { - break; - } - - lastEdge = edge; - itemPos++; - } - } - - final int numHeaders = getHeaderViewsCount(); - final int numFooters = getFooterViewsCount(); - - boolean updated = false; - - int oldFirstExpPos = mFirstExpPos; - int oldSecondExpPos = mSecondExpPos; - float oldSlideFrac = mSlideFrac; - - if (mAnimate) { - int edgeToEdge = Math.abs(edge - lastEdge); - - int edgeTop, edgeBottom; - if (mFloatViewMid < edge) { - edgeBottom = edge; - edgeTop = lastEdge; - } else { - edgeTop = edge; - edgeBottom = lastEdge; - } - // Log.d("mobeta", "edgeTop="+edgeTop+" edgeBot="+edgeBottom); - - int slideRgnHeight = (int) (0.5f * mSlideRegionFrac * edgeToEdge); - float slideRgnHeightF = slideRgnHeight; - int slideEdgeTop = edgeTop + slideRgnHeight; - int slideEdgeBottom = edgeBottom - slideRgnHeight; - - // Three regions - if (mFloatViewMid < slideEdgeTop) { - mFirstExpPos = itemPos - 1; - mSecondExpPos = itemPos; - mSlideFrac = 0.5f * (slideEdgeTop - mFloatViewMid) / slideRgnHeightF; - // Log.d("mobeta", - // "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); - } else if (mFloatViewMid < slideEdgeBottom) { - mFirstExpPos = itemPos; - mSecondExpPos = itemPos; - } else { - mFirstExpPos = itemPos; - mSecondExpPos = itemPos + 1; - mSlideFrac = 0.5f * (1.0f + (edgeBottom - mFloatViewMid) / slideRgnHeightF); - // Log.d("mobeta", - // "firstExp="+mFirstExpPos+" secExp="+mSecondExpPos+" slideFrac="+mSlideFrac); - } - - } else { - mFirstExpPos = itemPos; - mSecondExpPos = itemPos; - } - - // correct for headers and footers - if (mFirstExpPos < numHeaders) { - itemPos = numHeaders; - mFirstExpPos = itemPos; - mSecondExpPos = itemPos; - } else if (mSecondExpPos >= getCount() - numFooters) { - itemPos = getCount() - numFooters - 1; - mFirstExpPos = itemPos; - mSecondExpPos = itemPos; - } - - if (mFirstExpPos != oldFirstExpPos || mSecondExpPos != oldSecondExpPos || mSlideFrac != oldSlideFrac) { - updated = true; - } - - if (itemPos != mFloatPos) { - if (mDragListener != null) { - mDragListener.drag(mFloatPos - numHeaders, itemPos - numHeaders); - } - - mFloatPos = itemPos; - updated = true; - } - - return updated; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mTrackDragSort) { - mDragSortTracker.appendState(); - } - } - - private class SmoothAnimator implements Runnable { - protected long mStartTime; - - private final float mDurationF; - - private final float mAlpha; - private final float mA, mB, mC; - - private float mD; - - private boolean mCanceled; - - public SmoothAnimator(float smoothness, int duration) { - mAlpha = smoothness; - mDurationF = duration; - mA = mD = 1f / (2f * mAlpha * (1f - mAlpha)); - mB = mAlpha / (2f * (mAlpha - 1f)); - mC = 1f / (1f - mAlpha); - } - - public float transform(float frac) { - if (frac < mAlpha) { - return mA * frac * frac; - } else if (frac < 1f - mAlpha) { - return mB + mC * frac; - } else { - return 1f - mD * (frac - 1f) * (frac - 1f); - } - } - - public void start() { - mStartTime = SystemClock.uptimeMillis(); - mCanceled = false; - onStart(); - post(this); - } - - public void cancel() { - mCanceled = true; - } - - public void onStart() { - // stub - } - - public void onUpdate(float frac, float smoothFrac) { - // stub - } - - public void onStop() { - // stub - } - - @Override - public void run() { - if (mCanceled) { - return; - } - - float fraction = (SystemClock.uptimeMillis() - mStartTime) / mDurationF; - - if (fraction >= 1f) { - onUpdate(1f, 1f); - onStop(); - } else { - onUpdate(fraction, transform(fraction)); - post(this); - } - } - } - - /** - * Centers floating View under touch point. - */ - private class LiftAnimator extends SmoothAnimator { - - private float mInitDragDeltaY; - private float mFinalDragDeltaY; - - public LiftAnimator(float smoothness, int duration) { - super(smoothness, duration); - } - - @Override - public void onStart() { - mInitDragDeltaY = mDragDeltaY; - mFinalDragDeltaY = mFloatViewHeightHalf; - } - - @Override - public void onUpdate(float frac, float smoothFrac) { - if (mDragState != DRAGGING) { - cancel(); - } else { - mDragDeltaY = (int) (smoothFrac * mFinalDragDeltaY + (1f - smoothFrac) * mInitDragDeltaY); - mFloatLoc.y = mY - mDragDeltaY; - doDragFloatView(true); - } - } - } - - /** - * Centers floating View over drop slot before destroying. - */ - private class DropAnimator extends SmoothAnimator { - - private int mDropPos; - private int srcPos; - private float mInitDeltaY; - private float mInitDeltaX; - - public DropAnimator(float smoothness, int duration) { - super(smoothness, duration); - } - - @Override - public void onStart() { - mDropPos = mFloatPos; - srcPos = mSrcPos; - mDragState = DROPPING; - mInitDeltaY = mFloatLoc.y - getTargetY(); - mInitDeltaX = mFloatLoc.x - getPaddingLeft(); - } - - private int getTargetY() { - final int first = getFirstVisiblePosition(); - final int otherAdjust = (mItemHeightCollapsed + getDividerHeight()) / 2; - View v = getChildAt(mDropPos - first); - int targetY = -1; - if (v != null) { - if (mDropPos == srcPos) { - targetY = v.getTop(); - } else if (mDropPos < srcPos) { - // expanded down - targetY = v.getTop() - otherAdjust; - } else { - // expanded up - targetY = v.getBottom() + otherAdjust - mFloatViewHeight; - } - } else { - // drop position is not on screen?? no animation - cancel(); - } - - return targetY; - } - - @Override - public void onUpdate(float frac, float smoothFrac) { - final int targetY = getTargetY(); - final int targetX = getPaddingLeft(); - final float deltaY = mFloatLoc.y - targetY; - final float deltaX = mFloatLoc.x - targetX; - final float f = 1f - smoothFrac; - if (f < Math.abs(deltaY / mInitDeltaY) || f < Math.abs(deltaX / mInitDeltaX)) { - mFloatLoc.y = targetY + (int) (mInitDeltaY * f); - mFloatLoc.x = getPaddingLeft() + (int) (mInitDeltaX * f); - doDragFloatView(true); - } - } - - @Override - public void onStop() { - dropFloatView(); - } - - } - - /** - * Collapses expanded items. - */ - private class RemoveAnimator extends SmoothAnimator { - - private float mFloatLocX; - private float mFirstStartBlank; - private float mSecondStartBlank; - - private int mFirstChildHeight = -1; - private int mSecondChildHeight = -1; - - private int mFirstPos; - private int mSecondPos; - private int srcPos; - - public RemoveAnimator(float smoothness, int duration) { - super(smoothness, duration); - } - - @Override - public void onStart() { - mFirstChildHeight = -1; - mSecondChildHeight = -1; - mFirstPos = mFirstExpPos; - mSecondPos = mSecondExpPos; - srcPos = mSrcPos; - mDragState = REMOVING; - - mFloatLocX = mFloatLoc.x; - if (mUseRemoveVelocity) { - float minVelocity = 2f * getWidth(); - if (mRemoveVelocityX == 0) { - mRemoveVelocityX = (mFloatLocX < 0 ? -1 : 1) * minVelocity; - } else { - minVelocity *= 2; - if (mRemoveVelocityX < 0 && mRemoveVelocityX > -minVelocity) - mRemoveVelocityX = -minVelocity; - else if (mRemoveVelocityX > 0 && mRemoveVelocityX < minVelocity) - mRemoveVelocityX = minVelocity; - } - } else { - destroyFloatView(); - } - } - - @Override - public void onUpdate(float frac, float smoothFrac) { - float f = 1f - smoothFrac; - - final int firstVis = getFirstVisiblePosition(); - View item = getChildAt(mFirstPos - firstVis); - ViewGroup.LayoutParams lp; - int blank; - - if (mUseRemoveVelocity) { - float dt = (float) (SystemClock.uptimeMillis() - mStartTime) / 1000; - if (dt == 0) - return; - float dx = mRemoveVelocityX * dt; - int w = getWidth(); - mRemoveVelocityX += (mRemoveVelocityX > 0 ? 1 : -1) * dt * w; - mFloatLocX += dx; - mFloatLoc.x = (int) mFloatLocX; - if (mFloatLocX < w && mFloatLocX > -w) { - mStartTime = SystemClock.uptimeMillis(); - doDragFloatView(true); - return; - } - } - - if (item != null) { - if (mFirstChildHeight == -1) { - mFirstChildHeight = getChildHeight(mFirstPos, item, false); - mFirstStartBlank = item.getHeight() - mFirstChildHeight; - } - blank = Math.max((int) (f * mFirstStartBlank), 1); - lp = item.getLayoutParams(); - lp.height = mFirstChildHeight + blank; - item.setLayoutParams(lp); - } - if (mSecondPos != mFirstPos) { - item = getChildAt(mSecondPos - firstVis); - if (item != null) { - if (mSecondChildHeight == -1) { - mSecondChildHeight = getChildHeight(mSecondPos, item, false); - mSecondStartBlank = item.getHeight() - mSecondChildHeight; - } - blank = Math.max((int) (f * mSecondStartBlank), 1); - lp = item.getLayoutParams(); - lp.height = mSecondChildHeight + blank; - item.setLayoutParams(lp); - } - } - } - - @Override - public void onStop() { - doRemoveItem(); - } - } - - public void removeItem(int which) { - - mUseRemoveVelocity = false; - removeItem(which, 0); - } - - /** - * Removes an item from the list and animates the removal. - * - * @param which - * Position to remove (NOTE: headers/footers ignored! this is a - * position in your input ListAdapter). - * @param velocityX - */ - public void removeItem(int which, float velocityX) { - if (mDragState == IDLE || mDragState == DRAGGING) { - - if (mDragState == IDLE) { - // called from outside drag-sort - mSrcPos = getHeaderViewsCount() + which; - mFirstExpPos = mSrcPos; - mSecondExpPos = mSrcPos; - mFloatPos = mSrcPos; - View v = getChildAt(mSrcPos - getFirstVisiblePosition()); - if (v != null) { - v.setVisibility(View.INVISIBLE); - } - } - - mDragState = REMOVING; - mRemoveVelocityX = velocityX; - - if (mInTouchEvent) { - switch (mCancelMethod) { - case ON_TOUCH_EVENT: - super.onTouchEvent(mCancelEvent); - break; - case ON_INTERCEPT_TOUCH_EVENT: - super.onInterceptTouchEvent(mCancelEvent); - break; - } - } - - if (mRemoveAnimator != null) { - mRemoveAnimator.start(); - } else { - doRemoveItem(which); - } - } - } - - /** - * Move an item, bypassing the drag-sort process. Simply calls through to - * {@link DropListener#drop(int, int)}. - * - * @param from - * Position to move (NOTE: headers/footers ignored! this is a - * position in your input ListAdapter). - * @param to - * Target position (NOTE: headers/footers ignored! this is a - * position in your input ListAdapter). - */ - public void moveItem(int from, int to) { - if (mDropListener != null) { - final int count = getInputAdapter().getCount(); - if (from >= 0 && from < count && to >= 0 && to < count) { - mDropListener.drop(from, to); - } - } - } - - /** - * Cancel a drag. Calls {@link #stopDrag(boolean, boolean)} with - * true as the first argument. - */ - public void cancelDrag() { - if (mDragState == DRAGGING) { - mDragScroller.stopScrolling(true); - destroyFloatView(); - clearPositions(); - adjustAllItems(); - - if (mInTouchEvent) { - mDragState = STOPPED; - } else { - mDragState = IDLE; - } - } - } - - private void clearPositions() { - mSrcPos = -1; - mFirstExpPos = -1; - mSecondExpPos = -1; - mFloatPos = -1; - } - - private void dropFloatView() { - // must set to avoid cancelDrag being called from the - // DataSetObserver - mDragState = DROPPING; - - if (mDropListener != null && mFloatPos >= 0 && mFloatPos < getCount()) { - final int numHeaders = getHeaderViewsCount(); - mDropListener.drop(mSrcPos - numHeaders, mFloatPos - numHeaders); - } - - destroyFloatView(); - - adjustOnReorder(); - clearPositions(); - adjustAllItems(); - - // now the drag is done - if (mInTouchEvent) { - mDragState = STOPPED; - } else { - mDragState = IDLE; - } - } - - private void doRemoveItem() { - doRemoveItem(mSrcPos - getHeaderViewsCount()); - } - - /** - * Removes dragged item from the list. Calls RemoveListener. - */ - private void doRemoveItem(int which) { - // must set to avoid cancelDrag being called from the - // DataSetObserver - mDragState = REMOVING; - - // end it - if (mRemoveListener != null) { - mRemoveListener.remove(which); - } - - destroyFloatView(); - - adjustOnReorder(); - clearPositions(); - - // now the drag is done - if (mInTouchEvent) { - mDragState = STOPPED; - } else { - mDragState = IDLE; - } - } - - private void adjustOnReorder() { - final int firstPos = getFirstVisiblePosition(); - // Log.d("mobeta", "first="+firstPos+" src="+mSrcPos); - if (mSrcPos < firstPos) { - // collapsed src item is off screen; - // adjust the scroll after item heights have been fixed - View v = getChildAt(0); - int top = 0; - if (v != null) { - top = v.getTop(); - } - // Log.d("mobeta", "top="+top+" fvh="+mFloatViewHeight); - setSelectionFromTop(firstPos - 1, top - getPaddingTop()); - } - } - - /** - * Stop a drag in progress. Pass true if you would like to - * remove the dragged item from the list. - * - * @param remove - * Remove the dragged item from the list. Calls a registered - * RemoveListener, if one exists. Otherwise, calls the - * DropListener, if one exists. - * - * @return True if the stop was successful. False if there is no floating - * View. - */ - public boolean stopDrag(boolean remove) { - mUseRemoveVelocity = false; - return stopDrag(remove, 0); - } - - public boolean stopDragWithVelocity(boolean remove, float velocityX) { - - mUseRemoveVelocity = true; - return stopDrag(remove, velocityX); - } - - public boolean stopDrag(boolean remove, float velocityX) { - if (mFloatView != null) { - mDragScroller.stopScrolling(true); - - if (remove) { - removeItem(mSrcPos - getHeaderViewsCount(), velocityX); - } else { - if (mDropAnimator != null) { - mDropAnimator.start(); - } else { - dropFloatView(); - } - } - - if (mTrackDragSort) { - mDragSortTracker.stopTracking(); - } - - return true; - } else { - // stop failed - return false; - } - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mIgnoreTouchEvent) { - mIgnoreTouchEvent = false; - return false; - } - - if (!mDragEnabled) { - return super.onTouchEvent(ev); - } - - boolean more = false; - - boolean lastCallWasIntercept = mLastCallWasIntercept; - mLastCallWasIntercept = false; - - if (!lastCallWasIntercept) { - saveTouchCoords(ev); - } - - // if (mFloatView != null) { - if (mDragState == DRAGGING) { - onDragTouchEvent(ev); - more = true; // give us more! - } else { - // what if float view is null b/c we dropped in middle - // of drag touch event? - - // if (mDragState != STOPPED) { - if (mDragState == IDLE) { - if (super.onTouchEvent(ev)) { - more = true; - } - } - - int action = ev.getAction() & MotionEvent.ACTION_MASK; - - switch (action) { - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - doActionUpOrCancel(); - break; - default: - if (more) { - mCancelMethod = ON_TOUCH_EVENT; - } - } - } - - return more; - } - - private void doActionUpOrCancel() { - mCancelMethod = NO_CANCEL; - mInTouchEvent = false; - if (mDragState == STOPPED) { - mDragState = IDLE; - } - mCurrFloatAlpha = mFloatAlpha; - mListViewIntercepted = false; - mChildHeightCache.clear(); - } - - private void saveTouchCoords(MotionEvent ev) { - int action = ev.getAction() & MotionEvent.ACTION_MASK; - if (action != MotionEvent.ACTION_DOWN) { - mLastX = mX; - mLastY = mY; - } - mX = (int) ev.getX(); - mY = (int) ev.getY(); - if (action == MotionEvent.ACTION_DOWN) { - mLastX = mX; - mLastY = mY; - } - mOffsetX = (int) ev.getRawX() - mX; - mOffsetY = (int) ev.getRawY() - mY; - } - - public boolean listViewIntercepted() { - return mListViewIntercepted; - } - - private boolean mListViewIntercepted = false; - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mDragEnabled) { - return super.onInterceptTouchEvent(ev); - } - - saveTouchCoords(ev); - mLastCallWasIntercept = true; - - int action = ev.getAction() & MotionEvent.ACTION_MASK; - - if (action == MotionEvent.ACTION_DOWN) { - if (mDragState != IDLE) { - // intercept and ignore - mIgnoreTouchEvent = true; - return true; - } - mInTouchEvent = true; - } - - boolean intercept = false; - - // the following deals with calls to super.onInterceptTouchEvent - if (mFloatView != null) { - // super's touch event canceled in startDrag - intercept = true; - } else { - if (super.onInterceptTouchEvent(ev)) { - mListViewIntercepted = true; - intercept = true; - } - - switch (action) { - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - doActionUpOrCancel(); - break; - default: - if (intercept) { - mCancelMethod = ON_TOUCH_EVENT; - } else { - mCancelMethod = ON_INTERCEPT_TOUCH_EVENT; - } - } - } - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - mInTouchEvent = false; - } - - return intercept; - } - - /** - * Set the width of each drag scroll region by specifying a fraction of the - * ListView height. - * - * @param heightFraction - * Fraction of ListView height. Capped at 0.5f. - * - */ - public void setDragScrollStart(float heightFraction) { - setDragScrollStarts(heightFraction, heightFraction); - } - - /** - * Set the width of each drag scroll region by specifying a fraction of the - * ListView height. - * - * @param upperFrac - * Fraction of ListView height for up-scroll bound. Capped at - * 0.5f. - * @param lowerFrac - * Fraction of ListView height for down-scroll bound. Capped at - * 0.5f. - * - */ - public void setDragScrollStarts(float upperFrac, float lowerFrac) { - if (lowerFrac > 0.5f) { - mDragDownScrollStartFrac = 0.5f; - } else { - mDragDownScrollStartFrac = lowerFrac; - } - - if (upperFrac > 0.5f) { - mDragUpScrollStartFrac = 0.5f; - } else { - mDragUpScrollStartFrac = upperFrac; - } - - if (getHeight() != 0) { - updateScrollStarts(); - } - } - - private void continueDrag(int x, int y) { - - // proposed position - mFloatLoc.x = x - mDragDeltaX; - mFloatLoc.y = y - mDragDeltaY; - - doDragFloatView(true); - - int minY = Math.min(y, mFloatViewMid + mFloatViewHeightHalf); - int maxY = Math.max(y, mFloatViewMid - mFloatViewHeightHalf); - - // get the current scroll direction - int currentScrollDir = mDragScroller.getScrollDir(); - - if (minY > mLastY && minY > mDownScrollStartY && currentScrollDir != DragScroller.DOWN) { - // dragged down, it is below the down scroll start and it is not - // scrolling up - - if (currentScrollDir != DragScroller.STOP) { - // moved directly from up scroll to down scroll - mDragScroller.stopScrolling(true); - } - - // start scrolling down - mDragScroller.startScrolling(DragScroller.DOWN); - } else if (maxY < mLastY && maxY < mUpScrollStartY && currentScrollDir != DragScroller.UP) { - // dragged up, it is above the up scroll start and it is not - // scrolling up - - if (currentScrollDir != DragScroller.STOP) { - // moved directly from down scroll to up scroll - mDragScroller.stopScrolling(true); - } - - // start scrolling up - mDragScroller.startScrolling(DragScroller.UP); - } else if (maxY >= mUpScrollStartY && minY <= mDownScrollStartY && mDragScroller.isScrolling()) { - // not in the upper nor in the lower drag-scroll regions but it is - // still scrolling - - mDragScroller.stopScrolling(true); - } - } - - private void updateScrollStarts() { - final int padTop = getPaddingTop(); - final int listHeight = getHeight() - padTop - getPaddingBottom(); - float heightF = listHeight; - - mUpScrollStartYF = padTop + mDragUpScrollStartFrac * heightF; - mDownScrollStartYF = padTop + (1.0f - mDragDownScrollStartFrac) * heightF; - - mUpScrollStartY = (int) mUpScrollStartYF; - mDownScrollStartY = (int) mDownScrollStartYF; - - mDragUpScrollHeight = mUpScrollStartYF - padTop; - mDragDownScrollHeight = padTop + listHeight - mDownScrollStartYF; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - updateScrollStarts(); - } - - private void adjustAllItems() { - final int first = getFirstVisiblePosition(); - final int last = getLastVisiblePosition(); - - int begin = Math.max(0, getHeaderViewsCount() - first); - int end = Math.min(last - first, getCount() - 1 - getFooterViewsCount() - first); - - for (int i = begin; i <= end; ++i) { - View v = getChildAt(i); - if (v != null) { - adjustItem(first + i, v, false); - } - } - } - - private void adjustItem(int position) { - View v = getChildAt(position - getFirstVisiblePosition()); - - if (v != null) { - adjustItem(position, v, false); - } - } - - /** - * Sets layout param height, gravity, and visibility on wrapped item. - */ - private void adjustItem(int position, View v, boolean invalidChildHeight) { - - // Adjust item height - ViewGroup.LayoutParams lp = v.getLayoutParams(); - int height; - if (position != mSrcPos && position != mFirstExpPos && position != mSecondExpPos) { - height = ViewGroup.LayoutParams.WRAP_CONTENT; - } else { - height = calcItemHeight(position, v, invalidChildHeight); - } - - if (height != lp.height) { - lp.height = height; - v.setLayoutParams(lp); - } - - // Adjust item gravity - if (position == mFirstExpPos || position == mSecondExpPos) { - if (position < mSrcPos) { - ((DragSortItemView) v).setGravity(Gravity.BOTTOM); - } else if (position > mSrcPos) { - ((DragSortItemView) v).setGravity(Gravity.TOP); - } - } - - // Finally adjust item visibility - - int oldVis = v.getVisibility(); - int vis = View.VISIBLE; - - if (position == mSrcPos && mFloatView != null) { - vis = View.INVISIBLE; - } - - if (vis != oldVis) { - v.setVisibility(vis); - } - } - - private int getChildHeight(int position) { - if (position == mSrcPos) { - return 0; - } - - View v = getChildAt(position - getFirstVisiblePosition()); - - if (v != null) { - // item is onscreen, therefore child height is valid, - // hence the "true" - return getChildHeight(position, v, false); - } else { - // item is offscreen - // first check cache for child height at this position - int childHeight = mChildHeightCache.get(position); - if (childHeight != -1) { - // Log.d("mobeta", "found child height in cache!"); - return childHeight; - } - - final ListAdapter adapter = getAdapter(); - int type = adapter.getItemViewType(position); - - // There might be a better place for checking for the following - final int typeCount = adapter.getViewTypeCount(); - if (typeCount != mSampleViewTypes.length) { - mSampleViewTypes = new View[typeCount]; - } - - if (type >= 0) { - if (mSampleViewTypes[type] == null) { - v = adapter.getView(position, null, this); - mSampleViewTypes[type] = v; - } else { - v = adapter.getView(position, mSampleViewTypes[type], this); - } - } else { - // type is HEADER_OR_FOOTER or IGNORE - v = adapter.getView(position, null, this); - } - - // current child height is invalid, hence "true" below - childHeight = getChildHeight(position, v, true); - - // cache it because this could have been expensive - mChildHeightCache.add(position, childHeight); - - return childHeight; - } - } - - private int getChildHeight(int position, View item, boolean invalidChildHeight) { - if (position == mSrcPos) { - return 0; - } - - View child; - if (position < getHeaderViewsCount() || position >= getCount() - getFooterViewsCount()) { - child = item; - } else { - child = ((ViewGroup) item).getChildAt(0); - } - - ViewGroup.LayoutParams lp = child.getLayoutParams(); - - if (lp != null) { - if (lp.height > 0) { - return lp.height; - } - } - - int childHeight = child.getHeight(); - - if (childHeight == 0 || invalidChildHeight) { - measureItem(child); - childHeight = child.getMeasuredHeight(); - } - - return childHeight; - } - - private int calcItemHeight(int position, View item, boolean invalidChildHeight) { - return calcItemHeight(position, getChildHeight(position, item, invalidChildHeight)); - } - - private int calcItemHeight(int position, int childHeight) { - - int divHeight = getDividerHeight(); - - boolean isSliding = mAnimate && mFirstExpPos != mSecondExpPos; - int maxNonSrcBlankHeight = mFloatViewHeight - mItemHeightCollapsed; - int slideHeight = (int) (mSlideFrac * maxNonSrcBlankHeight); - - int height; - - if (position == mSrcPos) { - if (mSrcPos == mFirstExpPos) { - if (isSliding) { - height = slideHeight + mItemHeightCollapsed; - } else { - height = mFloatViewHeight; - } - } else if (mSrcPos == mSecondExpPos) { - // if gets here, we know an item is sliding - height = mFloatViewHeight - slideHeight; - } else { - height = mItemHeightCollapsed; - } - } else if (position == mFirstExpPos) { - if (isSliding) { - height = childHeight + slideHeight; - } else { - height = childHeight + maxNonSrcBlankHeight; - } - } else if (position == mSecondExpPos) { - // we know an item is sliding (b/c 2ndPos != 1stPos) - height = childHeight + maxNonSrcBlankHeight - slideHeight; - } else { - height = childHeight; - } - - return height; - } - - @Override - public void requestLayout() { - if (!mBlockLayoutRequests) { - super.requestLayout(); - } - } - - private int adjustScroll(int movePos, View moveItem, int oldFirstExpPos, int oldSecondExpPos) { - int adjust = 0; - - final int childHeight = getChildHeight(movePos); - - int moveHeightBefore = moveItem.getHeight(); - int moveHeightAfter = calcItemHeight(movePos, childHeight); - - int moveBlankBefore = moveHeightBefore; - int moveBlankAfter = moveHeightAfter; - if (movePos != mSrcPos) { - moveBlankBefore -= childHeight; - moveBlankAfter -= childHeight; - } - - int maxBlank = mFloatViewHeight; - if (mSrcPos != mFirstExpPos && mSrcPos != mSecondExpPos) { - maxBlank -= mItemHeightCollapsed; - } - - if (movePos <= oldFirstExpPos) { - if (movePos > mFirstExpPos) { - adjust += maxBlank - moveBlankAfter; - } - } else if (movePos == oldSecondExpPos) { - if (movePos <= mFirstExpPos) { - adjust += moveBlankBefore - maxBlank; - } else if (movePos == mSecondExpPos) { - adjust += moveHeightBefore - moveHeightAfter; - } else { - adjust += moveBlankBefore; - } - } else { - if (movePos <= mFirstExpPos) { - adjust -= maxBlank; - } else if (movePos == mSecondExpPos) { - adjust -= moveBlankAfter; - } - } - - return adjust; - } - - private void measureItem(View item) { - ViewGroup.LayoutParams lp = item.getLayoutParams(); - if (lp == null) { - lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - item.setLayoutParams(lp); - } - int wspec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, getListPaddingLeft() + getListPaddingRight(), - lp.width); - int hspec; - if (lp.height > 0) { - hspec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - } else { - hspec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } - item.measure(wspec, hspec); - } - - private void measureFloatView() { - if (mFloatView != null) { - measureItem(mFloatView); - mFloatViewHeight = mFloatView.getMeasuredHeight(); - mFloatViewHeightHalf = mFloatViewHeight / 2; - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Log.d("mobeta", "onMeasure called"); - if (mFloatView != null) { - if (mFloatView.isLayoutRequested()) { - measureFloatView(); - } - mFloatViewOnMeasured = true; // set to false after layout - } - mWidthMeasureSpec = widthMeasureSpec; - } - - @Override - protected void layoutChildren() { - super.layoutChildren(); - - if (mFloatView != null) { - if (mFloatView.isLayoutRequested() && !mFloatViewOnMeasured) { - // Have to measure here when usual android measure - // pass is skipped. This happens during a drag-sort - // when layoutChildren is called directly. - measureFloatView(); - } - mFloatView.layout(0, 0, mFloatView.getMeasuredWidth(), mFloatView.getMeasuredHeight()); - mFloatViewOnMeasured = false; - } - } - - protected boolean onDragTouchEvent(MotionEvent ev) { - // we are in a drag - int action = ev.getAction() & MotionEvent.ACTION_MASK; - - switch (ev.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_CANCEL: - if (mDragState == DRAGGING) { - cancelDrag(); - } - doActionUpOrCancel(); - break; - case MotionEvent.ACTION_UP: - // Log.d("mobeta", "calling stopDrag from onDragTouchEvent"); - if (mDragState == DRAGGING) { - stopDrag(false); - } - doActionUpOrCancel(); - break; - case MotionEvent.ACTION_MOVE: - continueDrag((int) ev.getX(), (int) ev.getY()); - break; - } - - return true; - } - - private boolean mFloatViewInvalidated = false; - - private void invalidateFloatView() { - mFloatViewInvalidated = true; - } - - /** - * Start a drag of item at position using the registered - * FloatViewManager. Calls through to - * {@link #startDrag(int,View,int,int,int)} after obtaining the floating - * View from the FloatViewManager. - * - * @param position - * Item to drag. - * @param dragFlags - * Flags that restrict some movements of the floating View. For - * example, set dragFlags |= - * ~{@link #DRAG_NEG_X} to allow dragging the floating View in all - * directions except off the screen to the left. - * @param deltaX - * Offset in x of the touch coordinate from the left edge of the - * floating View (i.e. touch-x minus float View left). - * @param deltaY - * Offset in y of the touch coordinate from the top edge of the - * floating View (i.e. touch-y minus float View top). - * - * @return True if the drag was started, false otherwise. This - * startDrag will fail if we are not currently in a - * touch event, there is no registered FloatViewManager, or the - * FloatViewManager returns a null View. - */ - public boolean startDrag(int position, int dragFlags, int deltaX, int deltaY) { - if (!mInTouchEvent || mFloatViewManager == null) { - return false; - } - - View v = mFloatViewManager.onCreateFloatView(position); - - if (v == null) { - return false; - } else { - return startDrag(position, v, dragFlags, deltaX, deltaY); - } - - } - - /** - * Start a drag of item at position without using a - * FloatViewManager. - * - * @param position - * Item to drag. - * @param floatView - * Floating View. - * @param dragFlags - * Flags that restrict some movements of the floating View. For - * example, set dragFlags |= - * ~{@link #DRAG_NEG_X} to allow dragging the floating View in all - * directions except off the screen to the left. - * @param deltaX - * Offset in x of the touch coordinate from the left edge of the - * floating View (i.e. touch-x minus float View left). - * @param deltaY - * Offset in y of the touch coordinate from the top edge of the - * floating View (i.e. touch-y minus float View top). - * - * @return True if the drag was started, false otherwise. This - * startDrag will fail if we are not currently in a - * touch event, floatView is null, or there is a drag - * in progress. - */ - public boolean startDrag(int position, View floatView, int dragFlags, int deltaX, int deltaY) { - if (mDragState != IDLE || !mInTouchEvent || mFloatView != null || floatView == null || !mDragEnabled) { - return false; - } - - if (getParent() != null) { - getParent().requestDisallowInterceptTouchEvent(true); - } - - int pos = position + getHeaderViewsCount(); - mFirstExpPos = pos; - mSecondExpPos = pos; - mSrcPos = pos; - mFloatPos = pos; - - // mDragState = dragType; - mDragState = DRAGGING; - mDragFlags = 0; - mDragFlags |= dragFlags; - - mFloatView = floatView; - measureFloatView(); // sets mFloatViewHeight - - mDragDeltaX = deltaX; - mDragDeltaY = deltaY; - mDragStartY = mY; - - // updateFloatView(mX - mDragDeltaX, mY - mDragDeltaY); - mFloatLoc.x = mX - mDragDeltaX; - mFloatLoc.y = mY - mDragDeltaY; - - // set src item invisible - final View srcItem = getChildAt(mSrcPos - getFirstVisiblePosition()); - - if (srcItem != null) { - srcItem.setVisibility(View.INVISIBLE); - } - - if (mTrackDragSort) { - mDragSortTracker.startTracking(); - } - - // once float view is created, events are no longer passed - // to ListView - switch (mCancelMethod) { - case ON_TOUCH_EVENT: - super.onTouchEvent(mCancelEvent); - break; - case ON_INTERCEPT_TOUCH_EVENT: - super.onInterceptTouchEvent(mCancelEvent); - break; - } - - requestLayout(); - - if (mLiftAnimator != null) { - mLiftAnimator.start(); - } - - return true; - } - - private void doDragFloatView(boolean forceInvalidate) { - int movePos = getFirstVisiblePosition() + getChildCount() / 2; - View moveItem = getChildAt(getChildCount() / 2); - - if (moveItem == null) { - return; - } - - doDragFloatView(movePos, moveItem, forceInvalidate); - } - - private void doDragFloatView(int movePos, View moveItem, boolean forceInvalidate) { - mBlockLayoutRequests = true; - - updateFloatView(); - - int oldFirstExpPos = mFirstExpPos; - int oldSecondExpPos = mSecondExpPos; - - boolean updated = updatePositions(); - - if (updated) { - adjustAllItems(); - int scroll = adjustScroll(movePos, moveItem, oldFirstExpPos, oldSecondExpPos); - // Log.d("mobeta", " adjust scroll="+scroll); - - setSelectionFromTop(movePos, moveItem.getTop() + scroll - getPaddingTop()); - layoutChildren(); - } - - if (updated || forceInvalidate) { - invalidate(); - } - - mBlockLayoutRequests = false; - } - - /** - * Sets float View location based on suggested values and constraints set in - * mDragFlags. - */ - private void updateFloatView() { - - if (mFloatViewManager != null) { - mTouchLoc.set(mX, mY); - mFloatViewManager.onDragFloatView(mFloatView, mFloatLoc, mTouchLoc); - } - - final int floatX = mFloatLoc.x; - final int floatY = mFloatLoc.y; - - // restrict x motion - int padLeft = getPaddingLeft(); - if ((mDragFlags & DRAG_POS_X) == 0 && floatX > padLeft) { - mFloatLoc.x = padLeft; - } else if ((mDragFlags & DRAG_NEG_X) == 0 && floatX < padLeft) { - mFloatLoc.x = padLeft; - } - - // keep floating view from going past bottom of last header view - final int numHeaders = getHeaderViewsCount(); - final int numFooters = getFooterViewsCount(); - final int firstPos = getFirstVisiblePosition(); - final int lastPos = getLastVisiblePosition(); - - // Log.d("mobeta", - // "nHead="+numHeaders+" nFoot="+numFooters+" first="+firstPos+" last="+lastPos); - int topLimit = getPaddingTop(); - if (firstPos < numHeaders) { - topLimit = getChildAt(numHeaders - firstPos - 1).getBottom(); - } - if ((mDragFlags & DRAG_NEG_Y) == 0) { - if (firstPos <= mSrcPos) { - topLimit = Math.max(getChildAt(mSrcPos - firstPos).getTop(), topLimit); - } - } - // bottom limit is top of first footer View or - // bottom of last item in list - int bottomLimit = getHeight() - getPaddingBottom(); - if (lastPos >= getCount() - numFooters - 1) { - bottomLimit = getChildAt(getCount() - numFooters - 1 - firstPos).getBottom(); - } - if ((mDragFlags & DRAG_POS_Y) == 0) { - if (lastPos >= mSrcPos) { - bottomLimit = Math.min(getChildAt(mSrcPos - firstPos).getBottom(), bottomLimit); - } - } - - // Log.d("mobeta", "dragView top=" + (y - mDragDeltaY)); - // Log.d("mobeta", "limit=" + limit); - // Log.d("mobeta", "mDragDeltaY=" + mDragDeltaY); - - if (floatY < topLimit) { - mFloatLoc.y = topLimit; - } else if (floatY + mFloatViewHeight > bottomLimit) { - mFloatLoc.y = bottomLimit - mFloatViewHeight; - } - - // get y-midpoint of floating view (constrained to ListView bounds) - mFloatViewMid = mFloatLoc.y + mFloatViewHeightHalf; - } - - private void destroyFloatView() { - if (mFloatView != null) { - mFloatView.setVisibility(GONE); - if (mFloatViewManager != null) { - mFloatViewManager.onDestroyFloatView(mFloatView); - } - mFloatView = null; - invalidate(); - } - } - - /** - * Interface for customization of the floating View appearance and dragging - * behavior. Implement your own and pass it to {@link #setFloatViewManager}. - * If your own is not passed, the default {@link SimpleFloatViewManager} - * implementation is used. - */ - public interface FloatViewManager { - /** - * Return the floating View for item at position. - * DragSortListView will measure and layout this View for you, so feel - * free to just inflate it. You can help DSLV by setting some - * {@link ViewGroup.LayoutParams} on this View; otherwise it will set - * some for you (with a width of FILL_PARENT and a height of - * WRAP_CONTENT). - * - * @param position - * Position of item to drag (NOTE: position - * excludes header Views; thus, if you want to call - * {@link ListView#getChildAt(int)}, you will need to add - * {@link ListView#getHeaderViewsCount()} to the index). - * - * @return The View you wish to display as the floating View. - */ - public View onCreateFloatView(int position); - - /** - * Called whenever the floating View is dragged. Float View properties - * can be changed here. Also, the upcoming location of the float View - * can be altered by setting location.x and - * location.y. - * - * @param floatView - * The floating View. - * @param location - * The location (top-left; relative to DSLV top-left) at - * which the float View would like to appear, given the - * current touch location and the offset provided in - * {@link DragSortListView#startDrag}. - * @param touch - * The current touch location (relative to DSLV top-left). - * @param pendingScroll - */ - public void onDragFloatView(View floatView, Point location, Point touch); - - /** - * Called when the float View is dropped; lets you perform any necessary - * cleanup. The internal DSLV floating View reference is set to null - * immediately after this is called. - * - * @param floatView - * The floating View passed to - * {@link #onCreateFloatView(int)}. - */ - public void onDestroyFloatView(View floatView); - } - - public void setFloatViewManager(FloatViewManager manager) { - mFloatViewManager = manager; - } - - public void setDragListener(DragListener l) { - mDragListener = l; - } - - /** - * Allows for easy toggling between a DragSortListView and a regular old - * ListView. If enabled, items are draggable, where the drag init mode - * determines how items are lifted (see {@link setDragInitMode(int)}). If - * disabled, items cannot be dragged. - * - * @param enabled - * Set true to enable list item dragging - */ - public void setDragEnabled(boolean enabled) { - mDragEnabled = enabled; - } - - public boolean isDragEnabled() { - return mDragEnabled; - } - - /** - * This better reorder your ListAdapter! DragSortListView does not do this - * for you; doesn't make sense to. Make sure - * {@link BaseAdapter#notifyDataSetChanged()} or something like it is called - * in your implementation. Furthermore, if you have a choiceMode other than - * none and the ListAdapter does not return true for - * {@link ListAdapter#hasStableIds()}, you will need to call - * {@link #moveCheckState(int, int)} to move the check boxes along with the - * list items. - * - * @param l - */ - public void setDropListener(DropListener l) { - mDropListener = l; - } - - /** - * Probably a no-brainer, but make sure that your remove listener calls - * {@link BaseAdapter#notifyDataSetChanged()} or something like it. When an - * item removal occurs, DragSortListView relies on a redraw of all the items - * to recover invisible views and such. Strictly speaking, if you remove - * something, your dataset has changed... - * - * @param l - */ - public void setRemoveListener(RemoveListener l) { - mRemoveListener = l; - } - - public interface DragListener { - public void drag(int from, int to); - } - - /** - * Your implementation of this has to reorder your ListAdapter! Make sure to - * call {@link BaseAdapter#notifyDataSetChanged()} or something like it in - * your implementation. - * - * @author heycosmo - * - */ - public interface DropListener { - public void drop(int from, int to); - } - - /** - * Make sure to call {@link BaseAdapter#notifyDataSetChanged()} or something - * like it in your implementation. - * - * @author heycosmo - * - */ - public interface RemoveListener { - public void remove(int which); - } - - public interface DragSortListener extends DropListener, DragListener, RemoveListener { - } - - public void setDragSortListener(DragSortListener l) { - setDropListener(l); - setDragListener(l); - setRemoveListener(l); - } - - /** - * Completely custom scroll speed profile. Default increases linearly with - * position and is constant in time. Create your own by implementing - * {@link DragSortListView.DragScrollProfile}. - * - * @param ssp - */ - public void setDragScrollProfile(DragScrollProfile ssp) { - if (ssp != null) { - mScrollProfile = ssp; - } - } - - /** - * Use this to move the check state of an item from one position to another - * in a drop operation. If you have a choiceMode which is not none, this - * method must be called when the order of items changes in an underlying - * adapter which does not have stable IDs (see - * {@link ListAdapter#hasStableIds()}). This is because without IDs, the - * ListView has no way of knowing which items have moved where, and cannot - * update the check state accordingly. - *

- * A word of warning about a "feature" in Android that you may run into when - * dealing with movable list items: for an adapter that does have - * stable IDs, ListView will attempt to locate each item based on its ID and - * move the check state from the item's old position to the new position — - * which is all fine and good (and removes the need for calling this - * function), except for the half-baked approach. Apparently to save time in - * the naive algorithm used, ListView will only search for an ID in the - * close neighborhood of the old position. If the user moves an item too far - * (specifically, more than 20 rows away), ListView will give up and just - * force the item to be unchecked. So if there is a reasonable chance that - * the user will move items more than 20 rows away from the original - * position, you may wish to use an adapter with unstable IDs and call this - * method manually instead. - * - * @param from - * @param to - */ - public void moveCheckState(int from, int to) { - // This method runs in O(n log n) time (n being the number of list - // items). The bottleneck is the call to AbsListView.setItemChecked, - // which is O(log n) because of the binary search involved in calling - // SparseBooleanArray.put(). - // - // To improve on the average time, we minimize the number of calls to - // setItemChecked by only calling it for items that actually have a - // changed state. This is achieved by building a list containing the - // start and end of the "runs" of checked items, and then moving the - // runs. Note that moving an item from A to B is essentially a rotation - // of the range of items in [A, B]. Let's say we have - // . . U V X Y Z . . - // and move U after Z. This is equivalent to a rotation one step to the - // left within the range you are moving across: - // . . V X Y Z U . . - // - // So, to perform the move we enumerate all the runs within the move - // range, then rotate each run one step to the left or right (depending - // on move direction). For example, in the list: - // X X . X X X . X - // we have two runs. One begins at the last item of the list and wraps - // around to the beginning, ending at position 1. The second begins at - // position 3 and ends at position 5. To rotate a run, regardless of - // length, we only need to set a check mark at one end of the run, and - // clear a check mark at the other end: - // X . X X X . X X - SparseBooleanArray cip = getCheckedItemPositions(); - int rangeStart = from; - int rangeEnd = to; - if (to < from) { - rangeStart = to; - rangeEnd = from; - } - rangeEnd += 1; - - int[] runStart = new int[cip.size()]; - int[] runEnd = new int[cip.size()]; - int runCount = buildRunList(cip, rangeStart, rangeEnd, runStart, runEnd); - if (runCount == 1 && (runStart[0] == runEnd[0])) { - // Special case where all items are checked, we can never set any - // item to false like we do below. - return; - } - - if (from < to) { - for (int i = 0; i != runCount; i++) { - setItemChecked(rotate(runStart[i], -1, rangeStart, rangeEnd), true); - setItemChecked(rotate(runEnd[i], -1, rangeStart, rangeEnd), false); - } - - } else { - for (int i = 0; i != runCount; i++) { - setItemChecked(runStart[i], false); - setItemChecked(runEnd[i], true); - } - } - } - - /** - * Use this when an item has been deleted, to move the check state of all - * following items up one step. If you have a choiceMode which is not none, - * this method must be called when the order of items changes in an - * underlying adapter which does not have stable IDs (see - * {@link ListAdapter#hasStableIds()}). This is because without IDs, the - * ListView has no way of knowing which items have moved where, and cannot - * update the check state accordingly. - * - * See also further comments on {@link #moveCheckState(int, int)}. - * - * @param position - */ - public void removeCheckState(int position) { - SparseBooleanArray cip = getCheckedItemPositions(); - - if (cip.size() == 0) - return; - int[] runStart = new int[cip.size()]; - int[] runEnd = new int[cip.size()]; - int rangeStart = position; - int rangeEnd = cip.keyAt(cip.size() - 1) + 1; - int runCount = buildRunList(cip, rangeStart, rangeEnd, runStart, runEnd); - for (int i = 0; i != runCount; i++) { - if (!(runStart[i] == position || (runEnd[i] < runStart[i] && runEnd[i] > position))) { - // Only set a new check mark in front of this run if it does - // not contain the deleted position. If it does, we only need - // to make it one check mark shorter at the end. - setItemChecked(rotate(runStart[i], -1, rangeStart, rangeEnd), true); - } - setItemChecked(rotate(runEnd[i], -1, rangeStart, rangeEnd), false); - } - } - - private static int buildRunList(SparseBooleanArray cip, int rangeStart, int rangeEnd, int[] runStart, int[] runEnd) { - int runCount = 0; - - int i = findFirstSetIndex(cip, rangeStart, rangeEnd); - if (i == -1) - return 0; - - int position = cip.keyAt(i); - int currentRunStart = position; - int currentRunEnd = currentRunStart + 1; - for (i++; i < cip.size() && (position = cip.keyAt(i)) < rangeEnd; i++) { - if (!cip.valueAt(i)) // not checked => not interesting - continue; - if (position == currentRunEnd) { - currentRunEnd++; - } else { - runStart[runCount] = currentRunStart; - runEnd[runCount] = currentRunEnd; - runCount++; - currentRunStart = position; - currentRunEnd = position + 1; - } - } - - if (currentRunEnd == rangeEnd) { - // rangeStart and rangeEnd are equivalent positions so to be - // consistent we translate them to the same integer value. That way - // we can check whether a run covers the entire range by just - // checking if the start equals the end position. - currentRunEnd = rangeStart; - } - runStart[runCount] = currentRunStart; - runEnd[runCount] = currentRunEnd; - runCount++; - - if (runCount > 1) { - if (runStart[0] == rangeStart && runEnd[runCount - 1] == rangeStart) { - // The last run ends at the end of the range, and the first run - // starts at the beginning of the range. So they are actually - // part of the same run, except they wrap around the end of the - // range. To avoid adjacent runs, we need to merge them. - runStart[0] = runStart[runCount - 1]; - runCount--; - } - } - return runCount; - } - - private static int rotate(int value, int offset, int lowerBound, int upperBound) { - int windowSize = upperBound - lowerBound; - - value += offset; - if (value < lowerBound) { - value += windowSize; - } else if (value >= upperBound) { - value -= windowSize; - } - return value; - } - - private static int findFirstSetIndex(SparseBooleanArray sba, int rangeStart, int rangeEnd) { - int size = sba.size(); - int i = insertionIndexForKey(sba, rangeStart); - while (i < size && sba.keyAt(i) < rangeEnd && !sba.valueAt(i)) - i++; - if (i == size || sba.keyAt(i) >= rangeEnd) - return -1; - return i; - } - - private static int insertionIndexForKey(SparseBooleanArray sba, int key) { - int low = 0; - int high = sba.size(); - while (high - low > 0) { - int middle = (low + high) >> 1; - if (sba.keyAt(middle) < key) - low = middle + 1; - else - high = middle; - } - return low; - } - - /** - * Interface for controlling scroll speed as a function of touch position - * and time. Use - * {@link DragSortListView#setDragScrollProfile(DragScrollProfile)} to set - * custom profile. - * - * @author heycosmo - * - */ - public interface DragScrollProfile { - /** - * Return a scroll speed in pixels/millisecond. Always return a positive - * number. - * - * @param w - * Normalized position in scroll region (i.e. w \in [0,1]). - * Small w typically means slow scrolling. - * @param t - * Time (in milliseconds) since start of scroll (handy if you - * want scroll acceleration). - * @return Scroll speed at position w and time t in pixels/ms. - */ - float getSpeed(float w, long t); - } - - private class DragScroller implements Runnable { - - private boolean mAbort; - - private long mPrevTime; - private long mCurrTime; - - private int dy; - private float dt; - private long tStart; - private int scrollDir; - - public final static int STOP = -1; - public final static int UP = 0; - public final static int DOWN = 1; - - private float mScrollSpeed; // pixels per ms - - private boolean mScrolling = false; - - private int mLastHeader; - private int mFirstFooter; - - public boolean isScrolling() { - return mScrolling; - } - - public int getScrollDir() { - return mScrolling ? scrollDir : STOP; - } - - public DragScroller() { - } - - public void startScrolling(int dir) { - if (!mScrolling) { - // Debug.startMethodTracing("dslv-scroll"); - mAbort = false; - mScrolling = true; - tStart = SystemClock.uptimeMillis(); - mPrevTime = tStart; - scrollDir = dir; - post(this); - } - } - - public void stopScrolling(boolean now) { - if (now) { - removeCallbacks(this); - mScrolling = false; - } else { - mAbort = true; - } - - // Debug.stopMethodTracing(); - } - - @Override - public void run() { - if (mAbort) { - mScrolling = false; - return; - } - - // Log.d("mobeta", "scroll"); - - final int first = getFirstVisiblePosition(); - final int last = getLastVisiblePosition(); - final int count = getCount(); - final int padTop = getPaddingTop(); - final int listHeight = getHeight() - padTop - getPaddingBottom(); - - int minY = Math.min(mY, mFloatViewMid + mFloatViewHeightHalf); - int maxY = Math.max(mY, mFloatViewMid - mFloatViewHeightHalf); - - if (scrollDir == UP) { - View v = getChildAt(0); - // Log.d("mobeta", "vtop="+v.getTop()+" padtop="+padTop); - if (v == null) { - mScrolling = false; - return; - } else { - if (first == 0 && v.getTop() == padTop) { - mScrolling = false; - return; - } - } - mScrollSpeed = mScrollProfile.getSpeed((mUpScrollStartYF - maxY) / mDragUpScrollHeight, mPrevTime); - } else { - View v = getChildAt(last - first); - if (v == null) { - mScrolling = false; - return; - } else { - if (last == count - 1 && v.getBottom() <= listHeight + padTop) { - mScrolling = false; - return; - } - } - mScrollSpeed = -mScrollProfile.getSpeed((minY - mDownScrollStartYF) / mDragDownScrollHeight, mPrevTime); - } - - mCurrTime = SystemClock.uptimeMillis(); - dt = mCurrTime - mPrevTime; - - // dy is change in View position of a list item; i.e. positive dy - // means user is scrolling up (list item moves down the screen, - // remember - // y=0 is at top of View). - dy = Math.round(mScrollSpeed * dt); - - int movePos; - if (dy >= 0) { - dy = Math.min(listHeight, dy); - movePos = first; - } else { - dy = Math.max(-listHeight, dy); - movePos = last; - } - - final View moveItem = getChildAt(movePos - first); - int top = moveItem.getTop() + dy; - - if (movePos == 0 && top > padTop) { - top = padTop; - } - - // always do scroll - mBlockLayoutRequests = true; - - setSelectionFromTop(movePos, top - padTop); - layoutChildren(); - invalidate(); - - mBlockLayoutRequests = false; - - // scroll means relative float View movement - doDragFloatView(movePos, moveItem, false); - - mPrevTime = mCurrTime; - // Log.d("mobeta", " updated prevTime="+mPrevTime); - - post(this); - } - } - - private class DragSortTracker { - StringBuilder mBuilder = new StringBuilder(); - - File mFile; - - private int mNumInBuffer = 0; - private int mNumFlushes = 0; - - private boolean mTracking = false; - - public DragSortTracker() { - File root = Environment.getExternalStorageDirectory(); - mFile = new File(root, "dslv_state.txt"); - - if (!mFile.exists()) { - try { - mFile.createNewFile(); - Log.d("mobeta", "file created"); - } catch (IOException e) { - Log.w("mobeta", "Could not create dslv_state.txt"); - Log.d("mobeta", e.getMessage()); - } - } - - } - - public void startTracking() { - mBuilder.append("\n"); - mNumFlushes = 0; - mTracking = true; - } - - public void appendState() { - if (!mTracking) { - return; - } - - mBuilder.append("\n"); - final int children = getChildCount(); - final int first = getFirstVisiblePosition(); - mBuilder.append(" "); - for (int i = 0; i < children; ++i) { - mBuilder.append(first + i).append(","); - } - mBuilder.append("\n"); - - mBuilder.append(" "); - for (int i = 0; i < children; ++i) { - mBuilder.append(getChildAt(i).getTop()).append(","); - } - mBuilder.append("\n"); - mBuilder.append(" "); - for (int i = 0; i < children; ++i) { - mBuilder.append(getChildAt(i).getBottom()).append(","); - } - mBuilder.append("\n"); - - mBuilder.append(" ").append(mFirstExpPos).append("\n"); - mBuilder.append(" ") - .append(getItemHeight(mFirstExpPos) - getChildHeight(mFirstExpPos)) - .append("\n"); - mBuilder.append(" ").append(mSecondExpPos).append("\n"); - mBuilder.append(" ") - .append(getItemHeight(mSecondExpPos) - getChildHeight(mSecondExpPos)) - .append("\n"); - mBuilder.append(" ").append(mSrcPos).append("\n"); - mBuilder.append(" ").append(mFloatViewHeight + getDividerHeight()).append("\n"); - mBuilder.append(" ").append(getHeight()).append("\n"); - mBuilder.append(" ").append(mLastY).append("\n"); - mBuilder.append(" ").append(mFloatViewMid).append("\n"); - mBuilder.append(" "); - for (int i = 0; i < children; ++i) { - mBuilder.append(getShuffleEdge(first + i, getChildAt(i).getTop())).append(","); - } - mBuilder.append("\n"); - - mBuilder.append("\n"); - mNumInBuffer++; - - if (mNumInBuffer > 1000) { - flush(); - mNumInBuffer = 0; - } - } - - public void flush() { - if (!mTracking) { - return; - } - - // save to file on sdcard - try { - boolean append = true; - if (mNumFlushes == 0) { - append = false; - } - FileWriter writer = new FileWriter(mFile, append); - - writer.write(mBuilder.toString()); - mBuilder.delete(0, mBuilder.length()); - - writer.flush(); - writer.close(); - - mNumFlushes++; - } catch (IOException e) { - // do nothing - } - } - - public void stopTracking() { - if (mTracking) { - mBuilder.append("\n"); - flush(); - mTracking = false; - } - } - - } - -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java b/Clover/app/src/main/java/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java deleted file mode 100644 index 63fc5c5a..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2011 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 com.mobeta.android.dslv; - -import android.content.Context; -import android.database.Cursor; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -// taken from v4 rev. 10 ResourceCursorAdapter.java - -/** - * Static library support version of the framework's - * {@link android.widget.ResourceCursorAdapter}. Used to write apps that run on - * platforms prior to Android 3.0. When running on Android 3.0 or above, this - * implementation is still used; it does not try to switch to the framework's - * implementation. See the framework SDK documentation for a class overview. - */ -public abstract class ResourceDragSortCursorAdapter extends DragSortCursorAdapter { - private int mLayout; - - private int mDropDownLayout; - - private LayoutInflater mInflater; - - /** - * Constructor the enables auto-requery. - * - * @deprecated This option is discouraged, as it results in Cursor queries - * being performed on the application's UI thread and thus can - * cause poor responsiveness or even Application Not Responding - * errors. As an alternative, use - * {@link android.app.LoaderManager} with a - * {@link android.content.CursorLoader}. - * - * @param context - * The context where the ListView associated with this adapter is - * running - * @param layout - * resource identifier of a layout file that defines the views - * for this list item. Unless you override them later, this will - * define both the item views and the drop down views. - */ - @Deprecated - public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) { - super(context, c); - mLayout = mDropDownLayout = layout; - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Constructor with default behavior as per - * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is - * recommended you not use this, but instead - * {@link #ResourceCursorAdapter(Context, int, Cursor, int)}. When using - * this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER} will always be - * set. - * - * @param context - * The context where the ListView associated with this adapter is - * running - * @param layout - * resource identifier of a layout file that defines the views - * for this list item. Unless you override them later, this will - * define both the item views and the drop down views. - * @param c - * The cursor from which to get the data. - * @param autoRequery - * If true the adapter will call requery() on the cursor whenever - * it changes so the most recent data is always displayed. Using - * true here is discouraged. - */ - public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) { - super(context, c, autoRequery); - mLayout = mDropDownLayout = layout; - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Standard constructor. - * - * @param context - * The context where the ListView associated with this adapter is - * running - * @param layout - * Resource identifier of a layout file that defines the views - * for this list item. Unless you override them later, this will - * define both the item views and the drop down views. - * @param c - * The cursor from which to get the data. - * @param flags - * Flags used to determine the behavior of the adapter, as per - * {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}. - */ - public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, int flags) { - super(context, c, flags); - mLayout = mDropDownLayout = layout; - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - } - - /** - * Inflates view(s) from the specified XML file. - * - * @see android.widget.CursorAdapter#newView(android.content.Context, - * android.database.Cursor, ViewGroup) - */ - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(mLayout, parent, false); - } - - @Override - public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(mDropDownLayout, parent, false); - } - - /** - *

- * Sets the layout resource of the item views. - *

- * - * @param layout - * the layout resources used to create item views - */ - public void setViewResource(int layout) { - mLayout = layout; - } - - /** - *

- * Sets the layout resource of the drop down views. - *

- * - * @param dropDownLayout - * the layout resources used to create drop down views - */ - public void setDropDownViewResource(int dropDownLayout) { - mDropDownLayout = dropDownLayout; - } -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java b/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java deleted file mode 100644 index c01ecb61..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleDragSortCursorAdapter.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2006 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 com.mobeta.android.dslv; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -// taken from sdk/sources/android-16/android/widget/SimpleCursorAdapter.java - -/** - * An easy adapter to map columns from a cursor to TextViews or ImageViews - * defined in an XML file. You can specify which columns you want, which views - * you want to display the columns, and the XML file that defines the appearance - * of these views. - * - * Binding occurs in two phases. First, if a - * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available, - * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)} - * is invoked. If the returned value is true, binding has occured. If the - * returned value is false and the view to bind is a TextView, - * {@link #setViewText(TextView, String)} is invoked. If the returned value is - * false and the view to bind is an ImageView, - * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate - * binding can be found, an {@link IllegalStateException} is thrown. - * - * If this adapter is used with filtering, for instance in an - * {@link android.widget.AutoCompleteTextView}, you can use the - * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the - * {@link android.widget.FilterQueryProvider} interfaces to get control over the - * filtering process. You can refer to - * {@link #convertToString(android.database.Cursor)} and - * {@link #runQueryOnBackgroundThread(CharSequence)} for more information. - */ -public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter { - /** - * A list of columns containing the data to bind to the UI. This field - * should be made private, so it is hidden from the SDK. {@hide} - */ - protected int[] mFrom; - /** - * A list of View ids representing the views to which the data must be - * bound. This field should be made private, so it is hidden from the SDK. - * {@hide} - */ - protected int[] mTo; - - private int mStringConversionColumn = -1; - private CursorToStringConverter mCursorToStringConverter; - private ViewBinder mViewBinder; - - String[] mOriginalFrom; - - /** - * Constructor the enables auto-requery. - * - * @deprecated This option is discouraged, as it results in Cursor queries - * being performed on the application's UI thread and thus can - * cause poor responsiveness or even Application Not Responding - * errors. As an alternative, use - * {@link android.app.LoaderManager} with a - * {@link android.content.CursorLoader}. - */ - @Deprecated - public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { - super(context, layout, c); - mTo = to; - mOriginalFrom = from; - findColumns(c, from); - } - - /** - * Standard constructor. - * - * @param context - * The context where the ListView associated with this - * SimpleListItemFactory is running - * @param layout - * resource identifier of a layout file that defines the views - * for this list item. The layout file should include at least - * those named views defined in "to" - * @param c - * The database cursor. Can be null if the cursor is not - * available yet. - * @param from - * A list of column names representing the data to bind to the - * UI. Can be null if the cursor is not available yet. - * @param to - * The views that should display column in the "from" parameter. - * These should all be TextViews. The first N views in this list - * are given the values of the first N columns in the from - * parameter. Can be null if the cursor is not available yet. - * @param flags - * Flags used to determine the behavior of the adapter, as per - * {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}. - */ - public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { - super(context, layout, c, flags); - mTo = to; - mOriginalFrom = from; - findColumns(c, from); - } - - /** - * Binds all of the field names passed into the "to" parameter of the - * constructor with their corresponding cursor columns as specified in the - * "from" parameter. - * - * Binding occurs in two phases. First, if a - * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available, - * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)} - * is invoked. If the returned value is true, binding has occured. If the - * returned value is false and the view to bind is a TextView, - * {@link #setViewText(TextView, String)} is invoked. If the returned value - * is false and the view to bind is an ImageView, - * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate - * binding can be found, an {@link IllegalStateException} is thrown. - * - * @throws IllegalStateException - * if binding cannot occur - * - * @see android.widget.CursorAdapter#bindView(android.view.View, - * android.content.Context, android.database.Cursor) - * @see #getViewBinder() - * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder) - * @see #setViewImage(ImageView, String) - * @see #setViewText(TextView, String) - */ - @Override - public void bindView(View view, Context context, Cursor cursor) { - final ViewBinder binder = mViewBinder; - final int count = mTo.length; - final int[] from = mFrom; - final int[] to = mTo; - - for (int i = 0; i < count; i++) { - final View v = view.findViewById(to[i]); - if (v != null) { - boolean bound = false; - if (binder != null) { - bound = binder.setViewValue(v, cursor, from[i]); - } - - if (!bound) { - String text = cursor.getString(from[i]); - if (text == null) { - text = ""; - } - - if (v instanceof TextView) { - setViewText((TextView) v, text); - } else if (v instanceof ImageView) { - setViewImage((ImageView) v, text); - } else { - throw new IllegalStateException(v.getClass().getName() + " is not a " - + " view that can be bounds by this SimpleCursorAdapter"); - } - } - } - } - } - - /** - * Returns the {@link ViewBinder} used to bind data to views. - * - * @return a ViewBinder or null if the binder does not exist - * - * @see #bindView(android.view.View, android.content.Context, - * android.database.Cursor) - * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder) - */ - public ViewBinder getViewBinder() { - return mViewBinder; - } - - /** - * Sets the binder used to bind data to views. - * - * @param viewBinder - * the binder used to bind data to views, can be null to remove - * the existing binder - * - * @see #bindView(android.view.View, android.content.Context, - * android.database.Cursor) - * @see #getViewBinder() - */ - public void setViewBinder(ViewBinder viewBinder) { - mViewBinder = viewBinder; - } - - /** - * Called by bindView() to set the image for an ImageView but only if there - * is no existing ViewBinder or if the existing ViewBinder cannot handle - * binding to an ImageView. - * - * By default, the value will be treated as an image resource. If the value - * cannot be used as an image resource, the value is used as an image Uri. - * - * Intended to be overridden by Adapters that need to setFilter strings - * retrieved from the database. - * - * @param v - * ImageView to receive an image - * @param value - * the value retrieved from the cursor - */ - public void setViewImage(ImageView v, String value) { - try { - v.setImageResource(Integer.parseInt(value)); - } catch (NumberFormatException nfe) { - v.setImageURI(Uri.parse(value)); - } - } - - /** - * Called by bindView() to set the text for a TextView but only if there is - * no existing ViewBinder or if the existing ViewBinder cannot handle - * binding to a TextView. - * - * Intended to be overridden by Adapters that need to setFilter strings - * retrieved from the database. - * - * @param v - * TextView to receive text - * @param text - * the text to be set for the TextView - */ - public void setViewText(TextView v, String text) { - v.setText(text); - } - - /** - * Return the index of the column used to get a String representation of the - * Cursor. - * - * @return a valid index in the current Cursor or -1 - * - * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) - * @see #setStringConversionColumn(int) - * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) - * @see #getCursorToStringConverter() - */ - public int getStringConversionColumn() { - return mStringConversionColumn; - } - - /** - * Defines the index of the column in the Cursor used to get a String - * representation of that Cursor. The column is used to convert the Cursor - * to a String only when the current CursorToStringConverter is null. - * - * @param stringConversionColumn - * a valid index in the current Cursor or -1 to use the default - * conversion mechanism - * - * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) - * @see #getStringConversionColumn() - * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) - * @see #getCursorToStringConverter() - */ - public void setStringConversionColumn(int stringConversionColumn) { - mStringConversionColumn = stringConversionColumn; - } - - /** - * Returns the converter used to convert the filtering Cursor into a String. - * - * @return null if the converter does not exist or an instance of - * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} - * - * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) - * @see #getStringConversionColumn() - * @see #setStringConversionColumn(int) - * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) - */ - public CursorToStringConverter getCursorToStringConverter() { - return mCursorToStringConverter; - } - - /** - * Sets the converter used to convert the filtering Cursor into a String. - * - * @param cursorToStringConverter - * the Cursor to String converter, or null to remove the - * converter - * - * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) - * @see #getStringConversionColumn() - * @see #setStringConversionColumn(int) - * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) - */ - public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) { - mCursorToStringConverter = cursorToStringConverter; - } - - /** - * Returns a CharSequence representation of the specified Cursor as defined - * by the current CursorToStringConverter. If no CursorToStringConverter has - * been set, the String conversion column is used instead. If the conversion - * column is -1, the returned String is empty if the cursor is null or - * Cursor.toString(). - * - * @param cursor - * the Cursor to convert to a CharSequence - * - * @return a non-null CharSequence representing the cursor - */ - @Override - public CharSequence convertToString(Cursor cursor) { - if (mCursorToStringConverter != null) { - return mCursorToStringConverter.convertToString(cursor); - } else if (mStringConversionColumn > -1) { - return cursor.getString(mStringConversionColumn); - } - - return super.convertToString(cursor); - } - - /** - * Create a map from an array of strings to an array of column-id integers - * in cursor c. If c is null, the array will be discarded. - * - * @param c - * the cursor to find the columns from - * @param from - * the Strings naming the columns of interest - */ - private void findColumns(Cursor c, String[] from) { - if (c != null) { - int i; - int count = from.length; - if (mFrom == null || mFrom.length != count) { - mFrom = new int[count]; - } - for (i = 0; i < count; i++) { - mFrom[i] = c.getColumnIndexOrThrow(from[i]); - } - } else { - mFrom = null; - } - } - - @Override - public Cursor swapCursor(Cursor c) { - // super.swapCursor() will notify observers before we have - // a valid mapping, make sure we have a mapping before this - // happens - findColumns(c, mOriginalFrom); - return super.swapCursor(c); - } - - /** - * Change the cursor and change the column-to-view mappings at the same - * time. - * - * @param c - * The database cursor. Can be null if the cursor is not - * available yet. - * @param from - * A list of column names representing the data to bind to the - * UI. Can be null if the cursor is not available yet. - * @param to - * The views that should display column in the "from" parameter. - * These should all be TextViews. The first N views in this list - * are given the values of the first N columns in the from - * parameter. Can be null if the cursor is not available yet. - */ - public void changeCursorAndColumns(Cursor c, String[] from, int[] to) { - mOriginalFrom = from; - mTo = to; - // super.changeCursor() will notify observers before we have - // a valid mapping, make sure we have a mapping before this - // happens - findColumns(c, mOriginalFrom); - super.changeCursor(c); - } - - /** - * This class can be used by external clients of SimpleCursorAdapter to bind - * values fom the Cursor to views. - * - * You should use this class to bind values from the Cursor to views that - * are not directly supported by SimpleCursorAdapter or to change the way - * binding occurs for views supported by SimpleCursorAdapter. - * - * @see SimpleCursorAdapter#bindView(android.view.View, - * android.content.Context, android.database.Cursor) - * @see SimpleCursorAdapter#setViewImage(ImageView, String) - * @see SimpleCursorAdapter#setViewText(TextView, String) - */ - public static interface ViewBinder { - /** - * Binds the Cursor column defined by the specified index to the - * specified view. - * - * When binding is handled by this ViewBinder, this method must return - * true. If this method returns false, SimpleCursorAdapter will attempts - * to handle the binding on its own. - * - * @param view - * the view to bind the data to - * @param cursor - * the cursor to get the data from - * @param columnIndex - * the column at which the data can be found in the cursor - * - * @return true if the data was bound to the view, false otherwise - */ - boolean setViewValue(View view, Cursor cursor, int columnIndex); - } - - /** - * This class can be used by external clients of SimpleCursorAdapter to - * define how the Cursor should be converted to a String. - * - * @see android.widget.CursorAdapter#convertToString(android.database.Cursor) - */ - public static interface CursorToStringConverter { - /** - * Returns a CharSequence representing the specified Cursor. - * - * @param cursor - * the cursor for which a CharSequence representation is - * requested - * - * @return a non-null CharSequence representing the cursor - */ - CharSequence convertToString(Cursor cursor); - } - -} diff --git a/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleFloatViewManager.java b/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleFloatViewManager.java deleted file mode 100644 index 27dd8328..00000000 --- a/Clover/app/src/main/java/com/mobeta/android/dslv/SimpleFloatViewManager.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.mobeta.android.dslv; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Point; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.ListView; - -/** - * Simple implementation of the FloatViewManager class. Uses list items as they - * appear in the ListView to create the floating View. - */ -public class SimpleFloatViewManager implements DragSortListView.FloatViewManager { - - private Bitmap mFloatBitmap; - - private ImageView mImageView; - - private int mFloatBGColor = Color.BLACK; - - private ListView mListView; - - public SimpleFloatViewManager(ListView lv) { - mListView = lv; - } - - public void setBackgroundColor(int color) { - mFloatBGColor = color; - } - - /** - * This simple implementation creates a Bitmap copy of the list item - * currently shown at ListView position. - */ - @Override - public View onCreateFloatView(int position) { - // Guaranteed that this will not be null? I think so. Nope, got - // a NullPointerException once... - View v = mListView.getChildAt(position + mListView.getHeaderViewsCount() - mListView.getFirstVisiblePosition()); - - if (v == null) { - return null; - } - - v.setPressed(false); - - // Create a copy of the drawing cache so that it does not get - // recycled by the framework when the list tries to clean up memory - //v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); - v.setDrawingCacheEnabled(true); - mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache()); - v.setDrawingCacheEnabled(false); - - if (mImageView == null) { - mImageView = new ImageView(mListView.getContext()); - } - mImageView.setBackgroundColor(mFloatBGColor); - mImageView.setPadding(0, 0, 0, 0); - mImageView.setImageBitmap(mFloatBitmap); - mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight())); - - return mImageView; - } - - /** - * This does nothing - */ - @Override - public void onDragFloatView(View floatView, Point position, Point touch) { - // do nothing - } - - /** - * Removes the Bitmap from the ImageView created in onCreateFloatView() and - * tells the system to recycle it. - */ - @Override - public void onDestroyFloatView(View floatView) { - ((ImageView) floatView).setImageDrawable(null); - - mFloatBitmap.recycle(); - mFloatBitmap = null; - } - -} diff --git a/Clover/app/src/main/java/org/floens/chan/Chan.java b/Clover/app/src/main/java/org/floens/chan/Chan.java new file mode 100644 index 00000000..1c0986bd --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/Chan.java @@ -0,0 +1,220 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan; + +import android.app.Application; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.StrictMode; +import android.view.ViewConfiguration; + +import com.android.volley.RequestQueue; +import com.android.volley.toolbox.ImageLoader; +import com.android.volley.toolbox.Volley; +import com.squareup.leakcanary.LeakCanary; +import com.squareup.leakcanary.RefWatcher; + +import org.floens.chan.chan.ChanUrls; +import org.floens.chan.core.cache.FileCache; +import org.floens.chan.core.manager.BoardManager; +import org.floens.chan.core.manager.WatchManager; +import org.floens.chan.core.net.BitmapLruImageCache; +import org.floens.chan.core.reply.ReplyManager; +import org.floens.chan.core.settings.ChanSettings; +import org.floens.chan.database.DatabaseManager; +import org.floens.chan.utils.AndroidUtils; +import org.floens.chan.utils.Logger; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.Locale; + +import de.greenrobot.event.EventBus; + +public class Chan extends Application { + private static final String TAG = "ChanApplication"; + + private static final long FILE_CACHE_DISK_SIZE = 50 * 1024 * 1024; + private static final String FILE_CACHE_NAME = "filecache"; + private static final int VOLLEY_LRU_CACHE_SIZE = 8 * 1024 * 1024; + private static final int VOLLEY_CACHE_SIZE = 10 * 1024 * 1024; + + public static Context con; + + private static Chan instance; + private static RequestQueue volleyRequestQueue; + private static com.android.volley.toolbox.ImageLoader imageLoader; + private static BoardManager boardManager; + private static WatchManager watchManager; + private static ReplyManager replyManager; + private static DatabaseManager databaseManager; + private static FileCache fileCache; + private static RefWatcher refWatcher; + + private String userAgent; + private int activityForegroundCounter = 0; + + public Chan() { + instance = this; + con = this; + } + + public static Chan getInstance() { + return instance; + } + + public static RequestQueue getVolleyRequestQueue() { + return volleyRequestQueue; + } + + public static ImageLoader getVolleyImageLoader() { + return imageLoader; + } + + public static BoardManager getBoardManager() { + return boardManager; + } + + public static WatchManager getWatchManager() { + return watchManager; + } + + public static ReplyManager getReplyManager() { + return replyManager; + } + + public static DatabaseManager getDatabaseManager() { + return databaseManager; + } + + public static FileCache getFileCache() { + return fileCache; + } + + public static RefWatcher getRefWatcher() { + return refWatcher; + } + + @Override + public void onCreate() { + super.onCreate(); + + // Force the overflow button to show, even on devices that have a + // physical button. + try { + ViewConfiguration config = ViewConfiguration.get(this); + Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); + if (menuKeyField != null) { + menuKeyField.setAccessible(true); + menuKeyField.setBoolean(config, false); + } + } catch (Exception e) { + } + + if (ChanBuild.DEVELOPER_MODE) { + refWatcher = LeakCanary.install(this); + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); + } + + AndroidUtils.init(); + + ChanUrls.loadScheme(ChanSettings.networkHttps.get()); + + // User agent is / + String version = ""; + try { + version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + version = version.toLowerCase(Locale.ENGLISH).replace(" ", "_"); + userAgent = getString(R.string.app_name) + "/" + version; + + cleanupOutdated(); + + File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir(); + + replyManager = new ReplyManager(this); + + volleyRequestQueue = Volley.newRequestQueue(this, getUserAgent(), null, new File(cacheDir, Volley.DEFAULT_CACHE_DIR), VOLLEY_CACHE_SIZE); + imageLoader = new ImageLoader(volleyRequestQueue, new BitmapLruImageCache(VOLLEY_LRU_CACHE_SIZE)); + + fileCache = new FileCache(new File(cacheDir, FILE_CACHE_NAME), FILE_CACHE_DISK_SIZE, getUserAgent()); + + databaseManager = new DatabaseManager(this); + boardManager = new BoardManager(); + watchManager = new WatchManager(this); + } + + public String getUserAgent() { + return userAgent; + } + + public void activityEnteredForeground() { + boolean lastForeground = getApplicationInForeground(); + + activityForegroundCounter++; + + if (getApplicationInForeground() != lastForeground) { + EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground())); + } + } + + public void activityEnteredBackground() { + boolean lastForeground = getApplicationInForeground(); + + activityForegroundCounter--; + if (activityForegroundCounter < 0) { + Logger.wtf(TAG, "activityForegroundCounter below 0"); + } + + if (getApplicationInForeground() != lastForeground) { + EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground())); + } + } + + public boolean getApplicationInForeground() { + return activityForegroundCounter > 0; + } + + private void cleanupOutdated() { + File ionCacheFolder = new File(getCacheDir() + "/ion"); + if (ionCacheFolder.exists() && ionCacheFolder.isDirectory()) { + Logger.i(TAG, "Clearing old ion folder"); + for (File file : ionCacheFolder.listFiles()) { + if (!file.delete()) { + Logger.i(TAG, "Could not delete old ion file " + file.getName()); + } + } + if (!ionCacheFolder.delete()) { + Logger.i(TAG, "Could not delete old ion folder"); + } else { + Logger.i(TAG, "Deleted old ion folder"); + } + } + } + + public static class ForegroundChangedMessage { + public boolean inForeground; + + public ForegroundChangedMessage(boolean inForeground) { + this.inForeground = inForeground; + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java b/Clover/app/src/main/java/org/floens/chan/ChanApplication.java index 2f137ced..4001cd19 100644 --- a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java +++ b/Clover/app/src/main/java/org/floens/chan/ChanApplication.java @@ -17,207 +17,5 @@ */ package org.floens.chan; -import android.app.Application; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.StrictMode; -import android.view.ViewConfiguration; - -import com.android.volley.RequestQueue; -import com.android.volley.toolbox.ImageLoader; -import com.android.volley.toolbox.Volley; -import com.squareup.leakcanary.LeakCanary; -import com.squareup.leakcanary.RefWatcher; - -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.manager.BoardManager; -import org.floens.chan.core.reply.ReplyManager; -import org.floens.chan.core.manager.WatchManager; -import org.floens.chan.core.net.BitmapLruImageCache; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.database.DatabaseManager; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.FileCache; -import org.floens.chan.utils.IconCache; -import org.floens.chan.utils.Logger; - -import java.io.File; -import java.lang.reflect.Field; -import java.util.Locale; - -import de.greenrobot.event.EventBus; - -public class ChanApplication extends Application { - private static final String TAG = "ChanApplication"; - - private static final long FILE_CACHE_DISK_SIZE = 50 * 1024 * 1024; - private static final String FILE_CACHE_NAME = "filecache"; - private static final int VOLLEY_LRU_CACHE_SIZE = 8 * 1024 * 1024; - private static final int VOLLEY_CACHE_SIZE = 10 * 1024 * 1024; - - public static Context con; - - private static ChanApplication instance; - private static RequestQueue volleyRequestQueue; - private static com.android.volley.toolbox.ImageLoader imageLoader; - private static BoardManager boardManager; - private static WatchManager watchManager; - private static ReplyManager replyManager; - private static DatabaseManager databaseManager; - private static FileCache fileCache; - private static RefWatcher refWatcher; - - private String userAgent; - private int activityForegroundCounter = 0; - - public ChanApplication() { - instance = this; - con = this; - } - - public static ChanApplication getInstance() { - return instance; - } - - public static RequestQueue getVolleyRequestQueue() { - return volleyRequestQueue; - } - - public static ImageLoader getVolleyImageLoader() { - return imageLoader; - } - - public static BoardManager getBoardManager() { - return boardManager; - } - - public static WatchManager getWatchManager() { - return watchManager; - } - - public static ReplyManager getReplyManager() { - return replyManager; - } - - public static DatabaseManager getDatabaseManager() { - return databaseManager; - } - - public static FileCache getFileCache() { - return fileCache; - } - - public static RefWatcher getRefWatcher() { - return refWatcher; - } - - @Override - public void onCreate() { - super.onCreate(); - - // Force the overflow button to show, even on devices that have a - // physical button. - try { - ViewConfiguration config = ViewConfiguration.get(this); - Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); - if (menuKeyField != null) { - menuKeyField.setAccessible(true); - menuKeyField.setBoolean(config, false); - } - } catch (Exception e) { - } - - if (ChanBuild.DEVELOPER_MODE) { - refWatcher = LeakCanary.install(this); - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); - } - - AndroidUtils.init(); - - ChanUrls.loadScheme(ChanSettings.getNetworkHttps()); - - // User agent is / - String version = ""; - try { - version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - version = version.toLowerCase(Locale.ENGLISH).replace(" ", "_"); - userAgent = getString(R.string.app_name) + "/" + version; - - IconCache.createIcons(this); - - cleanupOutdated(); - - File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir(); - - replyManager = new ReplyManager(this); - - volleyRequestQueue = Volley.newRequestQueue(this, getUserAgent(), null, new File(cacheDir, Volley.DEFAULT_CACHE_DIR), VOLLEY_CACHE_SIZE); - imageLoader = new ImageLoader(volleyRequestQueue, new BitmapLruImageCache(VOLLEY_LRU_CACHE_SIZE)); - - fileCache = new FileCache(new File(cacheDir, FILE_CACHE_NAME), FILE_CACHE_DISK_SIZE, getUserAgent()); - - databaseManager = new DatabaseManager(this); - boardManager = new BoardManager(); - watchManager = new WatchManager(this); - } - - public String getUserAgent() { - return userAgent; - } - - public void activityEnteredForeground() { - boolean lastForeground = getApplicationInForeground(); - - activityForegroundCounter++; - - if (getApplicationInForeground() != lastForeground) { - EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground())); - } - } - - public void activityEnteredBackground() { - boolean lastForeground = getApplicationInForeground(); - - activityForegroundCounter--; - if (activityForegroundCounter < 0) { - Logger.wtf(TAG, "activityForegroundCounter below 0"); - } - - if (getApplicationInForeground() != lastForeground) { - EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground())); - } - } - - public boolean getApplicationInForeground() { - return activityForegroundCounter > 0; - } - - private void cleanupOutdated() { - File ionCacheFolder = new File(getCacheDir() + "/ion"); - if (ionCacheFolder.exists() && ionCacheFolder.isDirectory()) { - Logger.i(TAG, "Clearing old ion folder"); - for (File file : ionCacheFolder.listFiles()) { - if (!file.delete()) { - Logger.i(TAG, "Could not delete old ion file " + file.getName()); - } - } - if (!ionCacheFolder.delete()) { - Logger.i(TAG, "Could not delete old ion folder"); - } else { - Logger.i(TAG, "Deleted old ion folder"); - } - } - } - - public static class ForegroundChangedMessage { - public boolean inForeground; - - public ForegroundChangedMessage(boolean inForeground) { - this.inForeground = inForeground; - } - } +public class ChanApplication extends Chan { } diff --git a/Clover/app/src/main/java/org/floens/chan/chan/ImageSearch.java b/Clover/app/src/main/java/org/floens/chan/chan/ImageSearch.java index 4c1df343..76029eb9 100644 --- a/Clover/app/src/main/java/org/floens/chan/chan/ImageSearch.java +++ b/Clover/app/src/main/java/org/floens/chan/chan/ImageSearch.java @@ -17,8 +17,6 @@ */ package org.floens.chan.chan; -import org.floens.chan.R; - import java.util.ArrayList; import java.util.List; @@ -34,7 +32,7 @@ public abstract class ImageSearch { static { engines.add(new ImageSearch() { public int getId() { - return R.id.action_0; + return 0; } public String getName() { @@ -48,7 +46,7 @@ public abstract class ImageSearch { engines.add(new ImageSearch() { public int getId() { - return R.id.action_1; + return 1; } public String getName() { diff --git a/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java b/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java index a1d37c8c..bc25f1ee 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/ControllerLogic.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.controller; import android.view.ViewGroup; diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java index 2475e711..ec3c3329 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/FadeInTransition.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.controller; import android.animation.Animator; diff --git a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java b/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java index c0ab0bed..30868e47 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/FadeOutTransition.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.controller; import android.animation.Animator; diff --git a/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java b/Clover/app/src/main/java/org/floens/chan/core/cache/FileCache.java similarity index 98% rename from Clover/app/src/main/java/org/floens/chan/utils/FileCache.java rename to Clover/app/src/main/java/org/floens/chan/core/cache/FileCache.java index 0fc17225..2b0af178 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java +++ b/Clover/app/src/main/java/org/floens/chan/core/cache/FileCache.java @@ -15,10 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.utils; - -import android.os.Handler; -import android.os.Looper; +package org.floens.chan.core.cache; import com.squareup.okhttp.Call; import com.squareup.okhttp.OkHttpClient; @@ -28,6 +25,10 @@ import com.squareup.okhttp.Response; import com.squareup.okhttp.ResponseBody; import com.squareup.okhttp.internal.Util; +import org.floens.chan.utils.AndroidUtils; +import org.floens.chan.utils.Logger; +import org.floens.chan.utils.Time; + import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.File; diff --git a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanLoader.java b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanLoader.java index 72f9d4af..8b8b0912 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanLoader.java +++ b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanLoader.java @@ -18,13 +18,11 @@ package org.floens.chan.core.loader; import android.text.TextUtils; -import android.util.SparseArray; import com.android.volley.Response; -import com.android.volley.ServerError; import com.android.volley.VolleyError; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; @@ -48,7 +46,6 @@ public class ChanLoader { private final List listeners = new ArrayList<>(); private final Loadable loadable; - private final SparseArray postsById = new SparseArray<>(); private ChanThread thread; private boolean destroyed = false; @@ -62,6 +59,10 @@ public class ChanLoader { public ChanLoader(Loadable loadable) { this.loadable = loadable; + + if (loadable.mode == Loadable.Mode.BOARD) { + loadable.mode = Loadable.Mode.CATALOG; + } } /** @@ -129,7 +130,7 @@ public class ChanLoader { request.cancel(); } - if (loadable.isBoardMode() || loadable.isCatalogMode()) { + if (loadable.isCatalogMode()) { loadable.no = 0; loadable.listViewIndex = 0; loadable.listViewTop = 0; @@ -147,16 +148,7 @@ public class ChanLoader { public void requestMoreData() { clearTimer(); - if (loadable.isBoardMode()) { - if (request != null) { - // finish the last board load first - return; - } - - loadable.no++; - - request = getData(); - } else if (loadable.isThreadMode()) { + if (loadable.isThreadMode()) { if (request != null) { return; } @@ -180,18 +172,12 @@ public class ChanLoader { return request != null; } - public Post findPostById(int id) { - return postsById.get(id); - } - public Loadable getLoadable() { return loadable; } /** * Get the time in milliseconds until another loadMore is recommended - * - * @return */ public long getTimeUntilLoadMore() { if (request != null) { @@ -272,7 +258,7 @@ public class ChanLoader { } ); - ChanApplication.getVolleyRequestQueue().add(request); + Chan.getVolleyRequestQueue().add(request); return request; } @@ -285,31 +271,8 @@ public class ChanLoader { thread = new ChanThread(loadable, new ArrayList()); } - if (loadable.isThreadMode() || loadable.isCatalogMode()) { - thread.posts.clear(); - thread.posts.addAll(result); - postsById.clear(); - for (Post post : result) { - postsById.append(post.no, post); - } - } else if (loadable.isBoardMode()) { - // Only add new posts - boolean flag; - for (Post post : result) { - flag = true; - for (Post cached : thread.posts) { - if (post.no == cached.no) { - flag = false; - break; - } - } - - if (flag) { - thread.posts.add(post); - postsById.append(post.no, post); - } - } - } + thread.posts.clear(); + thread.posts.addAll(result); if (loadable.isThreadMode() && thread.posts.size() > 0) { thread.op = thread.posts.get(0); @@ -346,11 +309,6 @@ public class ChanLoader { Logger.e(TAG, "Loading error"); - // 404 with more pages already loaded means endofline - if ((error instanceof ServerError) && loadable.isBoardMode() && loadable.no > 0) { - error = new EndOfLineException(); - } - clearTimer(); for (ChanLoaderCallback l : listeners) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java index 27892616..d62627d0 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java +++ b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java @@ -30,11 +30,11 @@ import android.text.style.StrikethroughSpan; import android.text.style.StyleSpan; import android.text.style.TypefaceSpan; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.model.Post; import org.floens.chan.core.model.PostLinkable; +import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.ThemeHelper; import org.jsoup.Jsoup; @@ -90,11 +90,11 @@ public class ChanParser { } private void parseSpans(Post post) { - boolean anonymize = ChanSettings.getAnonymize(); - boolean anonymizeIds = ChanSettings.getAnonymizeIds(); + boolean anonymize = ChanSettings.anonymize.get(); + boolean anonymizeIds = ChanSettings.anonymizeIds.get(); if (anonymize) { - post.name = ChanApplication.getInstance().getString(R.string.default_name); + post.name = "Anonymous"; post.tripcode = ""; } @@ -376,7 +376,7 @@ public class ChanParser { // Append You when it's a reply to an saved reply // todo synchronized - if (ChanApplication.getDatabaseManager().isSavedReply(post.board, id)) { + if (Chan.getDatabaseManager().isSavedReply(post.board, id)) { key += " (You)"; } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/loader/EndOfLineException.java b/Clover/app/src/main/java/org/floens/chan/core/loader/EndOfLineException.java deleted file mode 100644 index ae98a8be..00000000 --- a/Clover/app/src/main/java/org/floens/chan/core/loader/EndOfLineException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.core.loader; - -import com.android.volley.NetworkResponse; -import com.android.volley.VolleyError; - -@SuppressWarnings("serial") -public class EndOfLineException extends VolleyError { - public EndOfLineException(NetworkResponse networkResponse) { - super(networkResponse); - } - - public EndOfLineException() { - super(); - } - - @Override - public String getMessage() { - return "End of the line"; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java index d2bf5c6f..bcd36854 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/BoardManager.java @@ -20,7 +20,7 @@ package org.floens.chan.core.manager; import com.android.volley.Response; import com.android.volley.VolleyError; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.model.Board; import org.floens.chan.core.net.BoardsRequest; @@ -84,7 +84,7 @@ public class BoardManager { } public void updateSavedBoards() { - ChanApplication.getDatabaseManager().updateBoards(allBoards); + Chan.getDatabaseManager().updateBoards(allBoards); notifyChanged(); } @@ -103,12 +103,12 @@ public class BoardManager { private void storeBoards() { updateByValueMap(); - ChanApplication.getDatabaseManager().setBoards(allBoards); + Chan.getDatabaseManager().setBoards(allBoards); notifyChanged(); } private void loadBoards() { - allBoards = ChanApplication.getDatabaseManager().getBoards(); + allBoards = Chan.getDatabaseManager().getBoards(); if (allBoards.size() == 0) { Logger.d(TAG, "Loading default boards"); allBoards = getDefaultBoards(); @@ -149,7 +149,7 @@ public class BoardManager { } private void loadFromServer() { - ChanApplication.getVolleyRequestQueue().add( + Chan.getVolleyRequestQueue().add( new BoardsRequest(ChanUrls.getBoardsUrl(), new Response.Listener>() { @Override public void onResponse(List data) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java deleted file mode 100644 index 5de96cfc..00000000 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/ThreadManager.java +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.core.manager; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.FragmentTransaction; -import android.app.ProgressDialog; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.CheckBox; -import android.widget.LinearLayout; -import android.widget.PopupMenu; -import android.widget.Toast; - -import com.android.volley.VolleyError; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.loader.ChanLoader; -import org.floens.chan.core.loader.LoaderPool; -import org.floens.chan.core.reply.ReplyManager.DeleteListener; -import org.floens.chan.core.reply.ReplyManager.DeleteResponse; -import org.floens.chan.core.model.ChanThread; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Pin; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.model.PostLinkable; -import org.floens.chan.core.model.SavedReply; -import org.floens.chan.ui.activity.ReplyActivity; -import org.floens.chan.ui.fragment.PostRepliesFragment; -import org.floens.chan.ui.fragment.ReplyFragment; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.Logger; - -import java.util.ArrayList; -import java.util.List; - -import static org.floens.chan.utils.AndroidUtils.dp; - -/** - * All PostView's need to have this referenced. This manages some things like - * pages, starting and stopping of loading, handling linkables, replies popups - * etc. onDestroy, onStart and onStop must be called from the activity/fragment - */ -public class ThreadManager implements ChanLoader.ChanLoaderCallback { - public static enum ViewMode { - LIST, GRID - } - - private static final String TAG = "ThreadManager"; - - private final Activity activity; - private final ThreadManagerListener threadManagerListener; - private final List popupQueue = new ArrayList<>(); - private PostRepliesFragment currentPopupFragment; - private int highlightedPost = -1; - private int lastPost = -1; - private String highlightedId = null; - - private ChanLoader chanLoader; - - public ThreadManager(Activity activity, final ThreadManagerListener listener) { - this.activity = activity; - threadManagerListener = listener; - } - - public void onDestroy() { - unbindLoader(); - } - - public void onStart() { - if (chanLoader != null) { - if (isWatching()) { - chanLoader.setAutoLoadMore(true); - chanLoader.requestMoreDataAndResetTimer(); - } - } - } - - public void onStop() { - if (chanLoader != null) { - chanLoader.setAutoLoadMore(false); - } - } - - public void bindLoader(Loadable loadable) { - if (chanLoader != null) { - unbindLoader(); - } - - chanLoader = LoaderPool.getInstance().obtain(loadable, this); - if (isWatching()) { - chanLoader.setAutoLoadMore(true); - } - } - - public void unbindLoader() { - if (chanLoader != null) { - chanLoader.setAutoLoadMore(false); - LoaderPool.getInstance().release(chanLoader, this); - chanLoader = null; - } else { - Logger.e(TAG, "Loader already unbinded"); - } - - highlightedPost = -1; - lastPost = -1; - highlightedId = null; - } - - public void bottomPostViewed() { - if (chanLoader.getLoadable().isThreadMode() && chanLoader.getThread() != null && chanLoader.getThread().posts.size() > 0) { - chanLoader.getLoadable().lastViewed = chanLoader.getThread().posts.get(chanLoader.getThread().posts.size() - 1).no; - } - - Pin pin = ChanApplication.getWatchManager().findPinByLoadable(chanLoader.getLoadable()); - if (pin != null) { - pin.onBottomPostViewed(); - ChanApplication.getWatchManager().onPinsChanged(); - } - } - - public boolean isWatching() { - if (!chanLoader.getLoadable().isThreadMode()) { - return false; - } else if (!ChanSettings.getThreadAutoRefresh()) { - return false; - } else if (chanLoader.getThread() != null && chanLoader.getThread().closed) { - return false; - } else { - return true; - } - } - - public void requestData() { - if (chanLoader != null) { - chanLoader.requestData(); - } else { - Logger.e(TAG, "Loader null in requestData"); - } - } - - /** - * Called by postadapter and threadwatchcounterview.onclick - */ - public void requestNextData() { - if (chanLoader != null) { - chanLoader.requestMoreData(); - } else { - Logger.e(TAG, "Loader null in requestData"); - } - } - - @Override - public void onChanLoaderError(VolleyError error) { - threadManagerListener.onThreadLoadError(error); - } - - @Override - public void onChanLoaderData(ChanThread thread) { - if (!isWatching()) { - chanLoader.setAutoLoadMore(false); - } - - if (thread.posts.size() > 0) { - lastPost = thread.posts.get(thread.posts.size() - 1).no; - } - - threadManagerListener.onThreadLoaded(thread); - } - - public boolean hasLoader() { - return chanLoader != null; - } - - public Post findPostById(int id) { - if (chanLoader == null) - return null; - return chanLoader.findPostById(id); - } - - public Loadable getLoadable() { - if (chanLoader == null) - return null; - return chanLoader.getLoadable(); - } - - public ChanLoader getChanLoader() { - return chanLoader; - } - - public void onThumbnailClicked(Post post) { - threadManagerListener.onThumbnailClicked(post); - } - - public void onPostClicked(Post post) { - if (chanLoader != null) { - threadManagerListener.onPostClicked(post); - } - } - - public void showPostOptions(final Post post, PopupMenu popupMenu) { - Menu menu = popupMenu.getMenu(); - - if (chanLoader.getLoadable().isBoardMode() || chanLoader.getLoadable().isCatalogMode()) { - menu.add(Menu.NONE, 9, Menu.NONE, activity.getString(R.string.action_pin)); - } - -// if (chanLoader.getLoadable().isThreadMode()) { -// menu.add(Menu.NONE, 10, Menu.NONE, activity.getString(R.string.post_quick_reply)); -// } - - String[] baseOptions = activity.getResources().getStringArray(R.array.post_options); - for (int i = 0; i < baseOptions.length; i++) { - menu.add(Menu.NONE, i, Menu.NONE, baseOptions[i]); - } - - if (!TextUtils.isEmpty(post.id)) { - menu.add(Menu.NONE, 6, Menu.NONE, activity.getString(R.string.post_highlight_id)); - } - - // Only add the delete option when the post is a saved reply - if (ChanApplication.getDatabaseManager().isSavedReply(post.board, post.no)) { - menu.add(Menu.NONE, 7, Menu.NONE, activity.getString(R.string.delete)); - } - - if (ChanSettings.getDeveloper()) { - menu.add(Menu.NONE, 8, Menu.NONE, "Make this a saved reply"); - } - - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(final MenuItem item) { - switch (item.getItemId()) { - case 10: // Quick reply - openReply(false); - // Pass through - case 0: // Quote -// ChanApplication.getReplyManager().quote(post.no); - break; - case 1: // Quote inline -// ChanApplication.getReplyManager().quoteInline(post.no, post.comment.toString()); - break; - case 2: // Info - showPostInfo(post); - break; - case 3: // Show clickables - showPostLinkables(post); - break; - case 4: // Copy text - copyToClipboard(post.comment.toString()); - break; - case 5: // Report - AndroidUtils.openWebView(activity, "Report /" + post.board + "/" + post.no, ChanUrls.getReportUrl(post.board, post.no)); - break; - case 6: // Id - highlightedId = post.id; - threadManagerListener.onRefreshView(); - break; - case 7: // Delete - deletePost(post); - break; - case 8: // Save reply - ChanApplication.getDatabaseManager().saveReply(new SavedReply(post.board, post.no, "foo")); - break; - case 9: // Pin - ChanApplication.getWatchManager().addPin(post); - break; - } - return false; - } - }); - } - - public void openReply(boolean startInActivity) { - if (chanLoader == null) - return; - - if (startInActivity) { - ReplyActivity.setLoadable(chanLoader.getLoadable()); - Intent i = new Intent(activity, ReplyActivity.class); - activity.startActivity(i); - } else { - ReplyFragment reply = ReplyFragment.newInstance(chanLoader.getLoadable(), true); - reply.show(activity.getFragmentManager(), "replyDialog"); - } - } - - public void onPostLinkableClicked(PostLinkable linkable) { - handleLinkableSelected(linkable); - } - - public void scrollToPost(int post) { - threadManagerListener.onScrollTo(post); - } - - public void highlightPost(int post) { - highlightedPost = post; - } - - public boolean isPostHightlighted(Post post) { - return (highlightedPost >= 0 && post.no == highlightedPost) || (highlightedId != null && post.id.equals(highlightedId)); - } - - public boolean isPostLastSeen(Post post) { - return post.no == chanLoader.getLoadable().lastViewed && post.no != lastPost; - } - - private void copyToClipboard(String comment) { - ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Post text", comment); - clipboard.setPrimaryClip(clip); - Toast.makeText(activity, R.string.post_text_copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } - - private void showPostInfo(Post post) { - String text = ""; - - if (post.hasImage) { - text += "File: " + post.filename + "." + post.ext + " \nDimensions: " + post.imageWidth + "x" - + post.imageHeight + "\nSize: " + AndroidUtils.getReadableFileSize(post.fileSize, false) + "\n\n"; - } - - text += "Time: " + post.date; - - if (!TextUtils.isEmpty(post.id)) { - text += "\nId: " + post.id; - } - - if (!TextUtils.isEmpty(post.tripcode)) { - text += "\nTripcode: " + post.tripcode; - } - - if (!TextUtils.isEmpty(post.countryName)) { - text += "\nCountry: " + post.countryName; - } - - if (!TextUtils.isEmpty(post.capcode)) { - text += "\nCapcode: " + post.capcode; - } - - AlertDialog dialog = new AlertDialog.Builder(activity).setTitle(R.string.post_info).setMessage(text) - .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }).create(); - - dialog.show(); - } - - /** - * Show a list of things that can be clicked in a list to the user. - * - * @param post The post that was clicked. - */ - public void showPostLinkables(Post post) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final ArrayList linkables = post.linkables; - - if (linkables.size() > 0) { - String[] keys = new String[linkables.size()]; - for (int i = 0; i < linkables.size(); i++) { - keys[i] = linkables.get(i).key; - } - - builder.setItems(keys, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - handleLinkableSelected(linkables.get(which)); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - } - - public void showPostReplies(Post post) { - RepliesPopup l = new RepliesPopup(); - List p = new ArrayList<>(); - for (int no : post.repliesFrom) { - Post r = findPostById(no); - if (r != null) { - p.add(r); - } - } - l.posts = p; - l.forNo = post.no; - if (p.size() > 0) { - showPostsRepliesFragment(l); - } - } - - public ThreadManager.ViewMode getViewMode() { - return threadManagerListener.getViewMode(); - } - - /** - * Handle when a linkable has been clicked. - * - * @param linkable the selected linkable. - */ - private void handleLinkableSelected(final PostLinkable linkable) { - if (linkable.type == PostLinkable.Type.QUOTE) { - Post post = findPostById((Integer) linkable.value); - if (post != null) { - RepliesPopup l = new RepliesPopup(); - l.forNo = (Integer) linkable.value; - l.posts.add(post); - showPostsRepliesFragment(l); - } - } else if (linkable.type == PostLinkable.Type.LINK) { - if (ChanSettings.getOpenLinkConfirmation()) { - new AlertDialog.Builder(activity) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - AndroidUtils.openLink((String) linkable.value); - } - }) - .setTitle(R.string.open_link_confirmation) - .setMessage((String) linkable.value) - .show(); - } else { - AndroidUtils.openLink((String) linkable.value); - } - } else if (linkable.type == PostLinkable.Type.THREAD) { - final PostLinkable.ThreadLink link = (PostLinkable.ThreadLink) linkable.value; - final Loadable thread = new Loadable(link.board, link.threadId); - - new AlertDialog.Builder(activity) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(final DialogInterface dialog, final int which) { - threadManagerListener.onOpenThread(thread, link.postId); - } - }) - .setTitle(R.string.open_thread_confirmation) - .setMessage("/" + thread.board + "/" + thread.no) - .show(); - } - } - - private void showPostsRepliesFragment(RepliesPopup repliesPopup) { - // Post popups are now queued up, more than 32 popups on top of each - // other makes the system crash! - popupQueue.add(repliesPopup); - - if (currentPopupFragment != null) { - currentPopupFragment.dismissNoCallback(); - } - -// PostRepliesFragment popup = PostRepliesFragment.newInstance(repliesPopup, this); - PostRepliesFragment popup = null; - FragmentTransaction ft = activity.getFragmentManager().beginTransaction(); - ft.add(popup, "postPopup"); - ft.commitAllowingStateLoss(); - - currentPopupFragment = popup; - } - - public void onPostRepliesPop() { - if (popupQueue.size() == 0) - return; - - popupQueue.remove(popupQueue.size() - 1); - - if (popupQueue.size() > 0) { -// PostRepliesFragment popup = PostRepliesFragment.newInstance(popupQueue.get(popupQueue.size() - 1), this); - PostRepliesFragment popup = null; - FragmentTransaction ft = activity.getFragmentManager().beginTransaction(); - ft.add(popup, "postPopup"); - ft.commit(); - - currentPopupFragment = popup; - } else { - currentPopupFragment = null; - } - } - - public void closeAllPostFragments() { - popupQueue.clear(); - currentPopupFragment = null; - } - - public boolean arePostRepliesOpen() { - return popupQueue.size() > 0; - } - - private void deletePost(final Post post) { - final CheckBox checkBox = new CheckBox(activity); - checkBox.setText(R.string.delete_image_only); - - LinearLayout wrapper = new LinearLayout(activity); - wrapper.addView(checkBox); - int padding = dp(8f); - wrapper.setPadding(padding, padding, padding, padding); - - new AlertDialog.Builder(activity).setTitle(R.string.delete_confirm).setView(wrapper) - .setPositiveButton(R.string.delete, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - doDeletePost(post, checkBox.isChecked()); - } - }).setNegativeButton(R.string.cancel, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }).show(); - } - - private void doDeletePost(Post post, boolean onlyImageDelete) { - SavedReply reply = ChanApplication.getDatabaseManager().getSavedReply(post.board, post.no); - if (reply == null) { - /* - * reply = new SavedReply(); reply.board = "g"; reply.no = 1234; - * reply.password = "boom"; - */ - return; - } - - final ProgressDialog dialog = ProgressDialog.show(activity, null, activity.getString(R.string.delete_wait)); - - ChanApplication.getReplyManager().postDelete(reply, onlyImageDelete, new DeleteListener() { - @Override - public void onResponse(DeleteResponse response) { - dialog.dismiss(); - - if (response.isNetworkError || response.isUserError) { - int resId; - - if (response.isTooSoonError) { - resId = R.string.delete_too_soon; - } else if (response.isInvalidPassword) { - resId = R.string.delete_password_incorrect; - } else if (response.isTooOldError) { - resId = R.string.delete_too_old; - } else { - resId = R.string.delete_fail; - } - - Toast.makeText(activity, resId, Toast.LENGTH_LONG).show(); - } else if (response.isSuccessful) { - Toast.makeText(activity, R.string.delete_success, Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(activity, R.string.delete_fail, Toast.LENGTH_LONG).show(); - } - } - }); - } - - public interface ThreadManagerListener { - void onThreadLoaded(ChanThread thread); - - void onThreadLoadError(VolleyError error); - - void onPostClicked(Post post); - - void onThumbnailClicked(Post post); - - void onScrollTo(int post); - - void onRefreshView(); - - void onOpenThread(Loadable thread, int highlightedPost); - - ThreadManager.ViewMode getViewMode(); - } - - public static class RepliesPopup { - public List posts = new ArrayList<>(); - public int listViewIndex; - public int listViewTop; - public int forNo = -1; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java index ff2ba790..f019ed6d 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java @@ -20,7 +20,7 @@ package org.floens.chan.core.manager; import android.content.Context; import android.content.Intent; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Post; @@ -51,7 +51,7 @@ public class WatchManager { public WatchManager(Context context) { this.context = context; - pins = ChanApplication.getDatabaseManager().getPinned(); + pins = Chan.getDatabaseManager().getPinned(); EventBus.getDefault().register(this); @@ -122,7 +122,7 @@ public class WatchManager { } pins.add(pin); - ChanApplication.getDatabaseManager().addPin(pin); + Chan.getDatabaseManager().addPin(pin); onPinsChanged(); @@ -161,7 +161,7 @@ public class WatchManager { public void removePin(Pin pin) { pins.remove(pin); pin.destroyWatcher(); - ChanApplication.getDatabaseManager().removePin(pin); + Chan.getDatabaseManager().removePin(pin); onPinsChanged(); @@ -174,7 +174,7 @@ public class WatchManager { * @param pin */ public void updatePin(Pin pin) { - ChanApplication.getDatabaseManager().updatePin(pin); + Chan.getDatabaseManager().updatePin(pin); onPinsChanged(); @@ -185,15 +185,15 @@ public class WatchManager { * Updates all the pins to the database. */ public void updateDatabase() { - ChanApplication.getDatabaseManager().updatePins(pins); + Chan.getDatabaseManager().updatePins(pins); } public void toggleWatch(Pin pin) { pin.watching = !pin.watching; EventBus.getDefault().post(new PinChangedMessage(pin)); - ChanApplication.getWatchManager().onPinsChanged(); - ChanApplication.getWatchManager().invokeLoadNow(); + Chan.getWatchManager().onPinsChanged(); + Chan.getWatchManager().invokeLoadNow(); } public void pinWatcherUpdated(Pin pin) { @@ -231,7 +231,7 @@ public class WatchManager { } } - public void onEvent(ChanApplication.ForegroundChangedMessage message) { + public void onEvent(Chan.ForegroundChangedMessage message) { updateNotificationServiceState(); updateTimerState(true); } @@ -259,11 +259,11 @@ public class WatchManager { } public boolean getWatchBackgroundEnabled() { - return ChanSettings.getWatchBackgroundEnabled(); + return ChanSettings.watchBackground.get(); } private void updatePinWatchers() { - updatePinWatchers(ChanSettings.getWatchEnabled()); + updatePinWatchers(ChanSettings.watchEnabled.get()); } private void updatePinWatchers(boolean watchEnabled) { @@ -294,9 +294,9 @@ public class WatchManager { } private void updateTimerState(boolean watchEnabled, boolean backgroundEnabled, boolean invokeLoadNow) { - Logger.d(TAG, "updateTimerState watchEnabled=" + watchEnabled + " backgroundEnabled=" + backgroundEnabled + " invokeLoadNow=" + invokeLoadNow + " foreground=" + ChanApplication.getInstance().getApplicationInForeground()); + Logger.d(TAG, "updateTimerState watchEnabled=" + watchEnabled + " backgroundEnabled=" + backgroundEnabled + " invokeLoadNow=" + invokeLoadNow + " foreground=" + Chan.getInstance().getApplicationInForeground()); if (watchEnabled) { - if (ChanApplication.getInstance().getApplicationInForeground()) { + if (Chan.getInstance().getApplicationInForeground()) { setTimer(invokeLoadNow ? 1 : FOREGROUND_TIME); } else { if (backgroundEnabled) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java b/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java index a20b8c23..95e44fd1 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Loadable.java @@ -57,22 +57,14 @@ public class Loadable { public Loadable() { } - /** - * Quick constructor for a board loadable. - * - * @param board - */ public Loadable(String board) { - mode = Mode.BOARD; + mode = Mode.CATALOG; this.board = board; - no = 0; + this.no = 0; } /** * Quick constructor for a thread loadable. - * - * @param board - * @param no */ public Loadable(String board, int no) { mode = Mode.THREAD; @@ -82,10 +74,6 @@ public class Loadable { /** * Quick constructor for a thread loadable with an title. - * - * @param board - * @param no - * @param title */ public Loadable(String board, int no, String title) { mode = Mode.THREAD; @@ -115,10 +103,6 @@ public class Loadable { return result; } - public boolean isBoardMode() { - return mode == Mode.BOARD; - } - public boolean isThreadMode() { return mode == Mode.THREAD; } @@ -127,26 +111,6 @@ public class Loadable { return mode == Mode.CATALOG; } - public void readFromBundle(Context context, Bundle bundle) { - String p = context.getPackageName(); - mode = bundle.getInt(p + ".mode", Mode.INVALID); - board = bundle.getString(p + ".board", ""); - no = bundle.getInt(p + ".no", -1); - title = bundle.getString(p + ".subject", ""); - listViewIndex = bundle.getInt(p + ".listViewIndex"); - listViewTop = bundle.getInt(p + ".listViewTop"); - } - - public void writeToBundle(Context context, Bundle bundle) { - String p = context.getPackageName(); - bundle.putInt(p + ".mode", mode); - bundle.putString(p + ".board", board); - bundle.putInt(p + ".no", no); - bundle.putString(p + ".subject", title); - bundle.putInt(p + ".listViewIndex", listViewIndex); - bundle.putInt(p + ".listViewTop", listViewTop); - } - public void readFromBundle(Context context, String tag, Bundle bundle) { String p = context.getPackageName(); mode = bundle.getInt(p + "." + tag + ".mode", Mode.INVALID); diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java index fb355c02..65e31e93 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java @@ -20,7 +20,7 @@ package org.floens.chan.core.model; import android.text.SpannableString; import android.text.TextUtils; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.loader.ChanParser; import org.jsoup.parser.Parser; @@ -131,7 +131,7 @@ public class Post { filename = Parser.unescapeEntities(filename, false); if (spoiler) { - Board b = ChanApplication.getBoardManager().getBoardByValue(board); + Board b = Chan.getBoardManager().getBoardByValue(board); if (b != null && b.customSpoilers >= 0) { thumbnailUrl = ChanUrls.getCustomSpoilerUrl(board, random.nextInt(b.customSpoilers) + 1); } else { @@ -143,7 +143,7 @@ public class Post { } if (!TextUtils.isEmpty(country)) { - Board b = ChanApplication.getBoardManager().getBoardByValue(board); + Board b = Chan.getBoardManager().getBoardByValue(board); countryUrl = b.trollFlags ? ChanUrls.getTrollCountryFlagUrl(country) : ChanUrls.getCountryFlagUrl(country); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/PostImage.java b/Clover/app/src/main/java/org/floens/chan/core/model/PostImage.java index 9bb03b4b..2de647b1 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/PostImage.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/PostImage.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.model; public class PostImage { diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/ByteArrayRequest.java b/Clover/app/src/main/java/org/floens/chan/core/net/ByteArrayRequest.java deleted file mode 100644 index 15fffacd..00000000 --- a/Clover/app/src/main/java/org/floens/chan/core/net/ByteArrayRequest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.core.net; - -import com.android.volley.NetworkResponse; -import com.android.volley.Request; -import com.android.volley.Response; -import com.android.volley.Response.ErrorListener; -import com.android.volley.Response.Listener; - -/** - * Request a plain byte[] Warning: no caching! - */ -public class ByteArrayRequest extends Request { - protected final Listener listener; - - public ByteArrayRequest(String url, Listener listener, ErrorListener errorListener) { - super(Method.GET, url, errorListener); - - this.listener = listener; - } - - @Override - protected void deliverResponse(byte[] response) { - listener.onResponse(response); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - return Response.success(response.data, null); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java index e8e4e27a..6ca1f964 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java +++ b/Clover/app/src/main/java/org/floens/chan/core/net/ChanReaderRequest.java @@ -22,7 +22,7 @@ import android.util.JsonReader; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; @@ -44,9 +44,7 @@ public class ChanReaderRequest extends JsonReaderRequest> { public static ChanReaderRequest newInstance(Loadable loadable, List cached, Listener> listener, ErrorListener errorListener) { String url; - if (loadable.isBoardMode()) { - url = ChanUrls.getPageUrl(loadable.board, loadable.no); - } else if (loadable.isThreadMode()) { + if (loadable.isThreadMode()) { url = ChanUrls.getThreadUrl(loadable.board, loadable.no); } else if (loadable.isCatalogMode()) { url = ChanUrls.getCatalogUrl(loadable.board); @@ -72,9 +70,7 @@ public class ChanReaderRequest extends JsonReaderRequest> { public List readJson(JsonReader reader) throws Exception { List list; - if (loadable.isBoardMode()) { - list = loadBoard(reader); - } else if (loadable.isThreadMode()) { + if (loadable.isThreadMode()) { list = loadThread(reader); } else if (loadable.isCatalogMode()) { list = loadCatalog(reader); @@ -177,7 +173,7 @@ public class ChanReaderRequest extends JsonReaderRequest> { } for (Post post : totalList) { - post.isSavedReply = ChanApplication.getDatabaseManager().isSavedReply(post.board, post.no); + post.isSavedReply = Chan.getDatabaseManager().isSavedReply(post.board, post.no); } return totalList; @@ -207,44 +203,6 @@ public class ChanReaderRequest extends JsonReaderRequest> { return list; } - private List loadBoard(JsonReader reader) throws Exception { - ArrayList list = new ArrayList<>(); - - reader.beginObject(); // Threads array - - if (reader.nextName().equals("threads")) { - reader.beginArray(); - - while (reader.hasNext()) { - reader.beginObject(); // Thread object - - if (reader.nextName().equals("posts")) { - reader.beginArray(); - - list.add(readPostObject(reader)); - - // Only consume one post - while (reader.hasNext()) - reader.skipValue(); - - reader.endArray(); - } else { - reader.skipValue(); - } - - reader.endObject(); - } - - reader.endArray(); - } else { - reader.skipValue(); - } - - reader.endObject(); - - return list; - } - private List loadCatalog(JsonReader reader) throws Exception { ArrayList list = new ArrayList<>(); diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/FileRequest.java b/Clover/app/src/main/java/org/floens/chan/core/net/FileRequest.java deleted file mode 100644 index b3421aa7..00000000 --- a/Clover/app/src/main/java/org/floens/chan/core/net/FileRequest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.core.net; - -import com.android.volley.NetworkResponse; -import com.android.volley.Request; -import com.android.volley.Response; -import com.android.volley.Response.ErrorListener; -import com.android.volley.Response.Listener; -import com.android.volley.toolbox.DiskBasedCache; -import com.android.volley.toolbox.HttpHeaderParser; - -import org.floens.chan.ChanApplication; - -import java.io.File; - -public class FileRequest extends Request { - protected final Listener listener; - - public FileRequest(String url, Listener listener, ErrorListener errorListener) { - super(Method.GET, url, errorListener); - this.listener = listener; - - setShouldCache(true); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - return Response.success(null, HttpHeaderParser.parseCacheHeaders(response)); - } - - @Override - protected void deliverResponse(Void response) { - DiskBasedCache cache = (DiskBasedCache) ChanApplication.getVolleyRequestQueue().getCache(); - File file = cache.getFileForKey(getCacheKey()); - - if (file.exists()) { - listener.onResponse(file); - } else { - listener.onResponse(null); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/net/JsonReaderRequest.java b/Clover/app/src/main/java/org/floens/chan/core/net/JsonReaderRequest.java index 095e7636..f32e1032 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/net/JsonReaderRequest.java +++ b/Clover/app/src/main/java/org/floens/chan/core/net/JsonReaderRequest.java @@ -27,8 +27,9 @@ import com.android.volley.Response.Listener; import com.android.volley.VolleyError; import com.android.volley.toolbox.HttpHeaderParser; +import org.floens.chan.utils.IOUtils; + import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; @@ -65,13 +66,7 @@ public abstract class JsonReaderRequest extends Request { exception = e; } - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + IOUtils.closeQuietly(reader); if (read == null) { if (exception != null) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java index 7b7c8803..b8237af0 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.presenter; import android.support.v4.view.ViewPager; diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java index 03abce65..9756ebb6 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ReplyPresenter.java @@ -1,8 +1,25 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.presenter; import android.text.TextUtils; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.manager.BoardManager; @@ -55,10 +72,10 @@ public class ReplyPresenter implements ReplyManager.FileListener, ReplyManager.H public ReplyPresenter(ReplyPresenterCallback callback) { this.callback = callback; - replyManager = ChanApplication.getReplyManager(); - boardManager = ChanApplication.getBoardManager(); - watchManager = ChanApplication.getWatchManager(); - databaseManager = ChanApplication.getDatabaseManager(); + replyManager = Chan.getReplyManager(); + boardManager = Chan.getBoardManager(); + watchManager = Chan.getWatchManager(); + databaseManager = Chan.getDatabaseManager(); } public void bindLoadable(Loadable loadable) { @@ -144,7 +161,7 @@ public class ReplyPresenter implements ReplyManager.FileListener, ReplyManager.H } previewOpen = false; } else { - ChanApplication.getReplyManager().pickFile(this); + Chan.getReplyManager().pickFile(this); pickingFile = true; } } @@ -313,7 +330,7 @@ public class ReplyPresenter implements ReplyManager.FileListener, ReplyManager.H String baseUrl = loadable.isThreadMode() ? ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no) : ChanUrls.getBoardUrlDesktop(loadable.board); - callback.initCaptcha(baseUrl, ChanUrls.getCaptchaSiteKey(), ChanApplication.getInstance().getUserAgent(), this); + callback.initCaptcha(baseUrl, ChanUrls.getCaptchaSiteKey(), Chan.getInstance().getUserAgent(), this); } break; } diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index 54e6b206..f0e65ee5 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java @@ -21,7 +21,7 @@ import android.text.TextUtils; import com.android.volley.VolleyError; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.loader.ChanLoader; @@ -72,8 +72,8 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt public ThreadPresenter(ThreadPresenterCallback threadPresenterCallback) { this.threadPresenterCallback = threadPresenterCallback; - watchManager = ChanApplication.getWatchManager(); - databaseManager = ChanApplication.getDatabaseManager(); + watchManager = Chan.getWatchManager(); + databaseManager = Chan.getDatabaseManager(); } public void bindLoadable(Loadable loadable) { @@ -287,7 +287,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt menu.add(new FloatingMenuItem(POST_OPTION_DELETE, R.string.delete)); } - if (ChanSettings.getDeveloper()) { + if (ChanSettings.developer.get()) { menu.add(new FloatingMenuItem(POST_OPTION_SAVE, "Save")); } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/reply/HttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/reply/HttpCall.java index fb359b57..c3d67922 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/reply/HttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/reply/HttpCall.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.reply; import com.squareup.okhttp.Callback; diff --git a/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyHttpCall.java b/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyHttpCall.java index 21642ac2..8fc369ee 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyHttpCall.java +++ b/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyHttpCall.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.reply; import android.text.TextUtils; diff --git a/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyManager.java b/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyManager.java index 533c801e..7468d996 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/reply/ReplyManager.java @@ -19,18 +19,14 @@ package org.floens.chan.core.reply; import android.content.Context; import android.content.Intent; -import android.text.TextUtils; import com.squareup.okhttp.Callback; import com.squareup.okhttp.FormEncodingBuilder; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.MultipartBuilder; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; -import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.Response; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.model.Loadable; @@ -38,18 +34,14 @@ import org.floens.chan.core.model.Reply; import org.floens.chan.core.model.SavedReply; import org.floens.chan.ui.activity.ImagePickActivity; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.Logger; import java.io.File; import java.io.IOException; import java.net.HttpCookie; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Random; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -63,7 +55,6 @@ public class ReplyManager { private final Context context; private FileListener fileListener; - private final Random random = new Random(); private OkHttpClient client; private Map drafts = new HashMap<>(); @@ -169,7 +160,7 @@ public class ReplyManager { public void onFailure(Request request, IOException e) { final PassResponse res = new PassResponse(); res.isError = true; - res.message = context.getString(R.string.pass_error); + res.message = context.getString(R.string.setting_pass_error); runUI(new Runnable() { public void run() { passListener.onResponse(res); @@ -317,131 +308,6 @@ public class ReplyManager { public String responseData = ""; } - public void postReply(Reply reply, ReplyListener replyListener) { - if (reply.usePass) { - postReplyInternal(reply, replyListener, null); - } else { - postReplyInternal(reply, replyListener, reply.captchaResponse); - } - } - - private void postReplyInternal(final Reply reply, final ReplyListener replyListener, String captchaHash) { -// reply.password = Long.toHexString(random.nextLong()); - - MultipartBuilder formBuilder = new MultipartBuilder(); - formBuilder.type(MultipartBuilder.FORM); - - formBuilder.addFormDataPart("mode", "regist"); -// formBuilder.addFormDataPart("pwd", reply.password); - - if (reply.resto >= 0) { - formBuilder.addFormDataPart("resto", String.valueOf(reply.resto)); - } - - formBuilder.addFormDataPart("name", reply.name); - formBuilder.addFormDataPart("email", reply.options); - - if (reply.resto >= 0 && !TextUtils.isEmpty(reply.subject)) { - formBuilder.addFormDataPart("sub", reply.subject); - } - - formBuilder.addFormDataPart("com", reply.comment); - - if (captchaHash != null) { - formBuilder.addFormDataPart("g-recaptcha-response", captchaHash); - } - - if (reply.file != null) { - formBuilder.addFormDataPart("upfile", reply.fileName, RequestBody.create( - MediaType.parse("application/octet-stream"), reply.file - )); - } - - if (reply.spoilerImage) { - formBuilder.addFormDataPart("spoiler", "on"); - } - - Request.Builder request = new Request.Builder() - .url(ChanUrls.getReplyUrl(reply.board)) - .post(formBuilder.build()); - - if (reply.usePass) { - request.addHeader("Cookie", "pass_id=" + reply.passId); - } - - makeOkHttpCall(request, new Callback() { - @Override - public void onFailure(Request request, IOException e) { - final ReplyResponse res = new ReplyResponse(); - res.isNetworkError = true; - - runUI(new Runnable() { - public void run() { - replyListener.onResponse(res); - } - }); - } - - @Override - public void onResponse(Response response) throws IOException { - final ReplyResponse res = new ReplyResponse(); - if (response.isSuccessful()) { - onReplyPosted(response.body().string(), reply, res); - response.body().close(); - } else { - res.isNetworkError = true; - } - - runUI(new Runnable() { - public void run() { - replyListener.onResponse(res); - } - }); - } - }); - } - - private ReplyResponse onReplyPosted(String responseString, Reply reply, ReplyResponse res) { - res.responseData = responseString; - - if (res.responseData.contains("No file selected")) { - res.isUserError = true; - res.isFileError = true; - } else if (res.responseData.contains("You forgot to solve the CAPTCHA") || res.responseData.contains("You seem to have mistyped the CAPTCHA")) { - res.isUserError = true; - res.isCaptchaError = true; - } else if (res.responseData.toLowerCase(Locale.ENGLISH).contains("post successful")) { - res.isSuccessful = true; - - Matcher matcher = POST_THREAD_NO_PATTERN.matcher(res.responseData); - int threadNo = -1; - int no = -1; - if (matcher.find()) { - try { - threadNo = Integer.parseInt(matcher.group(1)); - no = Integer.parseInt(matcher.group(2)); - } catch (NumberFormatException err) { - err.printStackTrace(); - } - } - - if (threadNo >= 0 && no >= 0) { - SavedReply savedReply = new SavedReply(); - savedReply.board = reply.board; - savedReply.no = no; -// savedReply.password = reply.password; - - ChanApplication.getDatabaseManager().saveReply(savedReply); - - res.threadNo = threadNo; - res.no = no; - } else { - Logger.w(TAG, "No thread & no in the response"); - } - } - return res; - } - public void makeHttpCall(HttpCall httpCall, HttpCallback callback) { //noinspection unchecked httpCall.setCallback(callback); @@ -450,7 +316,7 @@ public class ReplyManager { httpCall.setup(requestBuilder); - requestBuilder.header("User-Agent", ChanApplication.getInstance().getUserAgent()); + requestBuilder.header("User-Agent", Chan.getInstance().getUserAgent()); Request request = requestBuilder.build(); client.newCall(request).enqueue(httpCall); @@ -463,7 +329,7 @@ public class ReplyManager { } private void makeOkHttpCall(Request.Builder requestBuilder, Callback callback) { - requestBuilder.header("User-Agent", ChanApplication.getInstance().getUserAgent()); + requestBuilder.header("User-Agent", Chan.getInstance().getUserAgent()); Request request = requestBuilder.build(); client.newCall(request).enqueue(callback); } @@ -471,51 +337,4 @@ public class ReplyManager { private void runUI(Runnable runnable) { AndroidUtils.runOnUiThread(runnable); } - - public interface ReplyListener { - void onResponse(ReplyResponse response); - } - - public static class ReplyResponse { - /** - * No response from server. - */ - public boolean isNetworkError = false; - - /** - * Some user error, like no file or captcha wrong. - */ - public boolean isUserError = false; - - /** - * The userError was an fileError - */ - public boolean isFileError = false; - - /** - * The userError was an captchaError - */ - public boolean isCaptchaError = false; - - /** - * Received 'post successful' - */ - public boolean isSuccessful = false; - - /** - * Raw html from the response. Used to set html in an WebView to the - * client, when the error was not recognized by Clover. - */ - public String responseData = ""; - - /** - * The no the post has - */ - public int no = -1; - - /** - * The thread no the post has - */ - public int threadNo = -1; - } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/BooleanSetting.java b/Clover/app/src/main/java/org/floens/chan/core/settings/BooleanSetting.java index 31e82dea..fa17f65c 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/BooleanSetting.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/BooleanSetting.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.settings; import android.content.SharedPreferences; diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java index 8ea4078f..3763cb1a 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java @@ -20,7 +20,7 @@ package org.floens.chan.core.settings; import android.content.SharedPreferences; import android.os.Environment; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.chan.ChanUrls; import org.floens.chan.utils.AndroidUtils; @@ -98,217 +98,32 @@ public class ChanSettings { watchEnabled = new BooleanSetting(p, "preference_watch_enabled", false, new Setting.SettingCallback() { @Override public void onValueChange(Setting setting, Boolean value) { - ChanApplication.getWatchManager().onWatchEnabledChanged(value); + Chan.getWatchManager().onWatchEnabledChanged(value); } }); watchCountdown = new BooleanSetting(p, "preference_watch_countdown", false); watchBackground = new BooleanSetting(p, "preference_watch_background_enabled", false, new Setting.SettingCallback() { @Override public void onValueChange(Setting setting, Boolean value) { - ChanApplication.getWatchManager().onBackgroundWatchingChanged(value); + Chan.getWatchManager().onBackgroundWatchingChanged(value); } }); watchBackgroundTimeout = new StringSetting(p, "preference_watch_background_timeout", "60"); watchNotifyMode = new StringSetting(p, "preference_watch_notify_mode", "all"); - watchSound = new StringSetting(p, "preference_watch_sound", "all"); + watchSound = new StringSetting(p, "preference_watch_sound", "quotes"); watchLed = new StringSetting(p, "preference_watch_led", "ffffffff"); passToken = new StringSetting(p, "preference_pass_token", ""); passPin = new StringSetting(p, "preference_pass_pin", ""); passId = new StringSetting(p, "preference_pass_id", ""); - } - private static SharedPreferences p() { - return AndroidUtils.getPreferences(); + // Old (but possibly still in some users phone) + // preference_board_view_mode default "list" + // preference_board_editor_filler default false + // preference_pass_enabled default false } public static boolean passLoggedIn() { return passId.get().length() > 0; } - - public static boolean getOpenLinkConfirmation() { - return p().getBoolean("preference_open_link_confirmation", true); - } - - public static String getDefaultName() { - return p().getString("preference_default_name", ""); - } - - public static boolean getPinOnPost() { - return p().getBoolean("preference_pin_on_post", false); - } - - public static boolean getDeveloper() { - return p().getBoolean("preference_developer", false); - } - - public static void setDeveloper(boolean developer) { - p().edit().putBoolean("preference_developer", developer).commit(); - } - - public static File getImageSaveDirectory() { - String path = p().getString("preference_image_save_location", null); - File file; - if (path == null) { - file = new File(Environment.getExternalStorageDirectory() + File.separator + "Clover"); - } else { - file = new File(path); - } - - return file; - } - - public static void setImageSaveDirectory(File file) { - p().edit().putString("preference_image_save_location", file.getAbsolutePath()).commit(); - } - - public static boolean getImageSaveOriginalFilename() { - return p().getBoolean("preference_image_save_original", false); - } - - public static boolean getImageShareUrl() { - return p().getBoolean("preference_image_share_url", false); - } - - public static boolean getWatchEnabled() { - return p().getBoolean("preference_watch_enabled", false); - } - - /** - * This also calls updateRunningState on the PinnedService to start/stop the - * service as needed. - * - * @param enabled - */ - public static void setWatchEnabled(boolean enabled) { - if (getWatchEnabled() != enabled) { - p().edit().putBoolean("preference_watch_enabled", enabled).commit(); - ChanApplication.getWatchManager().onWatchEnabledChanged(enabled); - } - } - - public static boolean getWatchCountdownVisibleEnabled() { - return p().getBoolean("preference_watch_countdown", false); - } - - public static boolean getWatchBackgroundEnabled() { - return p().getBoolean("preference_watch_background_enabled", false); - } - - public static int getWatchBackgroundTimeout() { - String number = p().getString("preference_watch_background_timeout", "60"); - return Integer.parseInt(number); - } - - public static String getWatchNotifyMode() { - return p().getString("preference_watch_notify_mode", "all"); - } - - public static String getWatchSound() { - return p().getString("preference_watch_sound", "quotes"); - } - - public static long getWatchLed() { - String raw = p().getString("preference_watch_led", "ffffffff"); - return Long.parseLong(raw, 16); - } - - public static boolean getVideoAutoPlay() { - return getImageAutoLoad() && !getVideoExternal() && p().getBoolean("preference_autoplay", false); - } - - public static boolean getThreadAutoRefresh() { - return p().getBoolean("preference_auto_refresh_thread", true); - } - - public static boolean getImageAutoLoad() { - return p().getBoolean("preference_image_auto_load", true); - } - - public static boolean getPassEnabled() { - return p().getBoolean("preference_pass_enabled", false); - } - - public static void setPassEnabled(boolean enabled) { - if (getPassEnabled() != enabled) { - p().edit().putBoolean("preference_pass_enabled", enabled).commit(); - } - } - - public static String getPassToken() { - return p().getString("preference_pass_token", ""); - } - - public static String getPassPin() { - return p().getString("preference_pass_pin", ""); - } - - public static void setPassId(String id) { - p().edit().putString("preference_pass_id", id).commit(); - } - - public static String getPassId() { - return p().getString("preference_pass_id", ""); - } - - public static String getTheme() { - return p().getString("preference_theme", "light"); - } - - public static boolean getForcePhoneLayout() { - return p().getBoolean("preference_force_phone_layout", false); - } - - public static boolean getBoardEditorFillerEnabled() { - return p().getBoolean("preference_board_editor_filler", false); - } - - public static boolean setBoardEditorFillerEnabled(boolean enabled) { - return p().edit().putBoolean("preference_board_editor_filler", enabled).commit(); - } - - public static String getBoardViewMode() { - return p().getString("preference_board_view_mode", "list"); - } - - public static void setBoardViewMode(String mode) { - p().edit().putString("preference_board_view_mode", mode).commit(); - } - - public static boolean getAnonymize() { - return p().getBoolean("preference_anonymize", false); - } - - public static boolean getAnonymizeIds() { - return p().getBoolean("preference_anonymize_ids", false); - } - - public static boolean getReplyButtonsBottom() { - return p().getBoolean("preference_buttons_bottom", false); - } - - public static String getBoardMode() { - return p().getString("preference_board_mode", "catalog"); - } - - public static boolean getVideoErrorIgnore() { - return p().getBoolean("preference_video_error_ignore", false); - } - - public static void setVideoErrorIgnore(boolean show) { - p().edit().putBoolean("preference_video_error_ignore", show).commit(); - } - - public static boolean getVideoExternal() { - return p().getBoolean("preference_video_external", false); - } - - public static int getFontSize() { - String font = p().getString("preference_font", null); - return font == null ? 14 : Integer.parseInt(font); - } - - public static boolean getNetworkHttps() { - return p().getBoolean("preference_network_https", true); - } } diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/Setting.java b/Clover/app/src/main/java/org/floens/chan/core/settings/Setting.java index e7c2c4f7..a2bf9cde 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/Setting.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/Setting.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.settings; import android.content.SharedPreferences; diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/StringSetting.java b/Clover/app/src/main/java/org/floens/chan/core/settings/StringSetting.java index df699062..bd507c82 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/StringSetting.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/StringSetting.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.core.settings; import android.content.SharedPreferences; diff --git a/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java b/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java index cef5a8d8..56712a92 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java +++ b/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java @@ -19,7 +19,7 @@ package org.floens.chan.core.watch; import com.android.volley.VolleyError; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.core.loader.ChanLoader; import org.floens.chan.core.loader.LoaderPool; import org.floens.chan.core.model.ChanThread; @@ -122,7 +122,7 @@ public class PinWatcher implements ChanLoader.ChanLoaderCallback { AndroidUtils.runOnUiThread(new Runnable() { @Override public void run() { - ChanApplication.getWatchManager().onPinsChanged(); + Chan.getWatchManager().onPinsChanged(); } }); } @@ -195,7 +195,7 @@ public class PinWatcher implements ChanLoader.ChanLoaderCallback { AndroidUtils.runOnUiThread(new Runnable() { @Override public void run() { - ChanApplication.getWatchManager().pinWatcherUpdated(pin); + Chan.getWatchManager().pinWatcherUpdated(pin); } }); } diff --git a/Clover/app/src/main/java/org/floens/chan/test/TestActivity.java b/Clover/app/src/main/java/org/floens/chan/test/TestActivity.java index 136a8013..d34ed167 100644 --- a/Clover/app/src/main/java/org/floens/chan/test/TestActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/test/TestActivity.java @@ -27,12 +27,12 @@ import android.widget.LinearLayout; import com.android.volley.VolleyError; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; +import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.loader.ChanLoader; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; -import org.floens.chan.utils.FileCache; import org.floens.chan.utils.Logger; import org.floens.chan.utils.ThemeHelper; @@ -90,7 +90,7 @@ public class TestActivity extends Activity implements View.OnClickListener { File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir(); File fileCacheDir = new File(cacheDir, "filecache"); - fileCache = new FileCache(fileCacheDir, 50 * 1024 * 1024, ChanApplication.getInstance().getUserAgent()); + fileCache = new FileCache(fileCacheDir, 50 * 1024 * 1024, Chan.getInstance().getUserAgent()); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/ThemeActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/ThemeActivity.java deleted file mode 100644 index 8e987953..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/ThemeActivity.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui; - -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.MenuItem; - -import org.floens.chan.R; -import org.floens.chan.utils.ThemeHelper; - -public class ThemeActivity extends AppCompatActivity { - private Toolbar toolbar; - - public void setTheme() { - setTheme(ThemeHelper.getInstance().getTheme().resValue); - } - - public void setToolbar() { - toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } else { - return super.onOptionsItemSelected(item); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/AboutActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/AboutActivity.java deleted file mode 100644 index 10f4c897..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/AboutActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.os.Bundle; -import android.webkit.WebView; - -import org.floens.chan.R; -import org.floens.chan.ui.ThemeActivity; - -public class AboutActivity extends ThemeActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - WebView webView = new WebView(this); - webView.loadUrl("file:///android_asset/html/licenses.html"); - - setContentView(webView); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java deleted file mode 100644 index 3bf2cab9..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/AdvancedSettingsActivity.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; - -import org.floens.chan.R; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.ui.fragment.FolderPickFragment; - -import java.io.File; - -public class AdvancedSettingsActivity extends ThemeActivity { - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - getFragmentManager().beginTransaction().replace(R.id.content, new AdvancedSettingsFragment()).commit(); - } - - public static class AdvancedSettingsFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preference_advanced); - - findPreference("preference_force_phone_layout").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, final Object newValue) { - BaseActivity.doRestartOnResume = true; - return true; - } - }); - - findPreference("preference_anonymize").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, final Object newValue) { - BaseActivity.doRestartOnResume = true; - return true; - } - }); - - findPreference("preference_anonymize_ids").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, final Object newValue) { - BaseActivity.doRestartOnResume = true; - return true; - } - }); - - final ListPreference boardMode = (ListPreference) findPreference("preference_board_mode"); - String currentModeValue = boardMode.getValue(); - if (currentModeValue == null) { - boardMode.setValue((String) boardMode.getEntryValues()[0]); - currentModeValue = boardMode.getValue(); - } - updateSummary(boardMode, currentModeValue); - boardMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateSummary(boardMode, newValue.toString()); - BaseActivity.doRestartOnResume = true; - return true; - } - }); - - reloadSavePath(); - final Preference saveLocation = findPreference("preference_image_save_location"); - saveLocation.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - File dir = ChanSettings.getImageSaveDirectory(); - dir.mkdirs(); - - FolderPickFragment frag = FolderPickFragment.newInstance(new FolderPickFragment.FolderPickListener() { - @Override - public void folderPicked(File path) { - ChanSettings.setImageSaveDirectory(path); - reloadSavePath(); - } - }, dir); - getActivity().getFragmentManager().beginTransaction().add(frag, null).commit(); - - return true; - } - }); - - findPreference("preference_network_https").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - ChanUrls.loadScheme((Boolean) newValue); - - return true; - } - }); - } - - private void reloadSavePath() { - Preference saveLocation = findPreference("preference_image_save_location"); - saveLocation.setSummary(ChanSettings.getImageSaveDirectory().getAbsolutePath()); - } - - private void updateSummary(ListPreference list, String value) { - int index = list.findIndexOfValue(value); - list.setSummary(list.getEntries()[index]); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java deleted file mode 100644 index b7e8497a..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.res.TypedArray; -import android.net.Uri; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.os.Bundle; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v4.widget.SlidingPaneLayout; -import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.widget.ShareActionProvider; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.EditText; -import android.widget.ListView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.model.ChanThread; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Pin; -import org.floens.chan.core.model.Post; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.ui.adapter.PinnedAdapter; -import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener; -import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener.DismissCallbacks; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.ThemeHelper; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public abstract class BaseActivity extends ThemeActivity implements PanelSlideListener { - public static boolean doRestartOnResume = false; - - private final static int ACTION_OPEN_URL = 1; - - protected PinnedAdapter pinnedAdapter; - protected DrawerLayout pinDrawer; - protected ListView pinDrawerView; - protected ActionBarDrawerToggle pinDrawerListener; - - protected SlidingPaneLayout threadPane; - - private String shareUrl; - private ShareActionProvider shareActionProvider; - private Intent pendingShareActionProviderIntent; - - /** - * Called when a post has been clicked in the pinned drawer - * - * @param post - */ - abstract public void openPin(Pin post); - - /** - * Called when a post has been clicked in the listview - * - * @param post - */ - abstract public void onOPClicked(Post post); - - abstract public void onOpenThread(Loadable thread); - - abstract public void onThreadLoaded(ChanThread thread); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - ThemeHelper.getInstance().reloadPostViewColors(this); - - setContentView(R.layout.activity_base); - - setTheme(); - setToolbar(); - - pinDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); - initDrawer(); - - threadPane = (SlidingPaneLayout) findViewById(R.id.pane_container); - initPane(); - - updateIcon(); - } - - @Override - public void onBackPressed() { - if (pinDrawer.isDrawerOpen(pinDrawerView)) { - pinDrawer.closeDrawer(pinDrawerView); - } else { - super.onBackPressed(); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - } - - @Override - protected void onResume() { - super.onResume(); - - if (doRestartOnResume) { - doRestartOnResume = false; - recreate(); - } - } - - private void initPane() { - threadPane.setPanelSlideListener(this); - threadPane.setParallaxDistance(dp(100)); - threadPane.setShadowResource(R.drawable.panel_shadow); - - TypedArray ta = obtainStyledAttributes(null, R.styleable.BoardPane, R.attr.board_pane_style, 0); - int color = ta.getColor(R.styleable.BoardPane_fade_color, 0); - ta.recycle(); - - threadPane.setSliderFadeColor(color); - threadPane.openPane(); - } - - protected void initDrawer() { - if (pinDrawerListener == null) { - return; - } - - pinDrawer.setDrawerListener(pinDrawerListener); - pinDrawer.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); - - pinDrawerView = (ListView) findViewById(R.id.left_drawer); - - pinnedAdapter = new PinnedAdapter(getSupportActionBar().getThemedContext(), pinDrawerView); // Get the dark theme, not the light one - pinnedAdapter.reload(); - pinDrawerView.setAdapter(pinnedAdapter); - - pinDrawerView.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Pin pin = pinnedAdapter.getItem(position); - if (pin == null) - return; - openPin(pin); - } - }); - - pinDrawerView.setOnItemLongClickListener(new OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - Pin post = pinnedAdapter.getItem(position); - if (post == null) - return false; - - onPinLongPress(post); - - return true; - } - }); - - SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(pinDrawerView, - new DismissCallbacks() { - @Override - public void onDismiss(ListView listView, int[] reverseSortedPositions) { - for (int position : reverseSortedPositions) { - removePin(pinnedAdapter.getItem(position)); - } - } - - @Override - public boolean canDismiss(int position) { - return pinnedAdapter.getItem(position) != null; - } - } - ); - - pinDrawerView.setOnTouchListener(touchListener); - pinDrawerView.setOnScrollListener(touchListener.makeScrollListener()); - pinDrawerView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - } - - private void updateIcon() { - /*List list = ChanApplication.getWatchManager().getWatchingPins(); - if (list.size() > 0) { - int count = 0; - boolean color = false; - for (Pin p : list) { - count += p.getNewPostCount(); - if (p.getNewQuoteCount() > 0) { - color = true; - } - } - - if (count > 0) { - Drawable icon = BadgeDrawable.get(getResources(), R.drawable.ic_launcher, count, color); - getSupportActionBar().setIcon(icon); - } else { - getSupportActionBar().setIcon(R.drawable.ic_launcher); - } - } else { - getSupportActionBar().setIcon(R.drawable.ic_launcher); - }*/ - } - - public void removePin(Pin pin) { - ChanApplication.getWatchManager().removePin(pin); - } - - public void updatePin(Pin pin) { - ChanApplication.getWatchManager().updatePin(pin); - } - - private void onPinLongPress(final Pin pin) { - new AlertDialog.Builder(this) - .setNegativeButton(R.string.drawer_pinned_delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // Delete pin - removePin(pin); - } - }).setPositiveButton(R.string.drawer_pinned_change_title, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // Change pin title - final EditText text = new EditText(BaseActivity.this); - text.setSingleLine(); - text.setText(pin.loadable.title); - text.setSelectAllOnFocus(true); - - AlertDialog titleDialog = new AlertDialog.Builder(BaseActivity.this) - .setPositiveButton(R.string.change, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - String value = text.getText().toString(); - - if (!TextUtils.isEmpty(value)) { - pin.loadable.title = value; - updatePin(pin); - } - } - }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - } - }).setTitle(R.string.drawer_pinned_change_title).setView(text).create(); - - AndroidUtils.requestKeyboardFocus(titleDialog, text); - - titleDialog.show(); - } - }).show(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_settings: - startActivity(new Intent(this, SettingsActivity.class)); - return true; - } - - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.base, menu); - /*shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.action_share)); - if (pendingShareActionProviderIntent != null) { - shareActionProvider.setShareIntent(pendingShareActionProviderIntent); - pendingShareActionProviderIntent = null; - }*/ - - return true; - } - - @Override - public void onPanelClosed(View view) { - } - - @Override - public void onPanelOpened(View view) { - } - - @Override - public void onPanelSlide(View view, float offset) { - } - - /** - * Set the url that Android Beam and the share action will send. - * - * @param url - */ - public void setShareUrl(String url) { - shareUrl = url; - - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - - if (adapter != null) { - NdefRecord record = null; - try { - record = NdefRecord.createUri(url); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - return; - } - - NdefMessage message = new NdefMessage(new NdefRecord[]{record}); - try { - adapter.setNdefPushMessage(message, this); - } catch (Exception e) { - } - } - - Intent share = new Intent(android.content.Intent.ACTION_SEND); - share.putExtra(android.content.Intent.EXTRA_TEXT, url); - share.setType("text/plain"); - - if (shareActionProvider != null) { - shareActionProvider.setShareIntent(share); - } else { - pendingShareActionProviderIntent = share; - } - } - - public void openInBrowser() { - if (shareUrl != null) { - showUrlOpenPicker(shareUrl); - } - } - - /** - * Let the user choose between all activities that can open the url. This is - * done to prevent "open in browser" opening the url in our own app. - * - * @param url - */ - public void showUrlOpenPicker(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - startActivity(intent); - } - - /** - * Used for showUrlOpenPicker - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == ACTION_OPEN_URL && resultCode == RESULT_OK && data != null) { - data.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(data); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardEditor.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardEditor.java deleted file mode 100644 index 20666d98..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/BoardEditor.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.Filter; -import android.widget.Filterable; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import com.mobeta.android.dslv.DragSortController; -import com.mobeta.android.dslv.DragSortListView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.manager.BoardManager; -import org.floens.chan.core.model.Board; -import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.utils.AndroidUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class BoardEditor extends ThemeActivity { - private final BoardManager boardManager = ChanApplication.getBoardManager(); - - private List list; - private DragSortListView listView; - private BoardEditAdapter adapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - list = boardManager.getSavedBoards(); - - adapter = new BoardEditAdapter(this, 0, list); - - listView = new DragSortListView(this, null); - listView.setAdapter(adapter); - listView.setDivider(new ColorDrawable(Color.TRANSPARENT)); - - final DragSortController controller = new NiceDragSortController(listView, adapter); - - listView.setFloatViewManager(controller); - listView.setOnTouchListener(controller); - listView.setDragEnabled(true); - listView.setDropListener(new DragSortListView.DropListener() { - @Override - public void drop(int from, int to) { - if (from != to) { - Board board = list.remove(from); - list.add(to, board); - adapter.notifyDataSetChanged(); - } - } - }); - - final SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(listView, - new SwipeDismissListViewTouchListener.DismissCallbacks() { - @Override - public void onDismiss(ListView listView, int[] reverseSortedPositions) { - for (int position : reverseSortedPositions) { - if (position >= 0 && position < adapter.getCount()) { - Board b = adapter.getItem(position); - adapter.remove(b); - b.saved = false; - } - } - - adapter.notifyDataSetChanged(); - } - - @Override - public boolean canDismiss(int position) { - return list.size() > 1; - } - } - ); - - listView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - return controller.onTouch(view, motionEvent) - || (!listView.isDragging() && touchListener.onTouch(view, motionEvent)); - } - }); - - listView.setOnScrollListener(touchListener.makeScrollListener()); - - listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) { - new AlertDialog.Builder(BoardEditor.this) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (position >= 0 && position < adapter.getCount()) { - Board b = adapter.getItem(position); - adapter.remove(b); - b.saved = false; - adapter.notifyDataSetChanged(); - } - } - }) - .setMessage(R.string.board_delete) - .show(); - return true; - } - }); - - ((ViewGroup) findViewById(R.id.content)).addView(listView); - } - - @Override - protected void onPause() { - super.onPause(); - - if (list.size() > 0) { - // Order - for (int i = 0; i < list.size(); i++) { - list.get(i).order = i; - } - - boardManager.updateSavedBoards(); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.board_edit, menu); - - menu.findItem(R.id.action_show_filler).setChecked(ChanSettings.getBoardEditorFillerEnabled()); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add_board: - showAddBoardDialog(); - return true; - case R.id.action_show_filler: - ChanSettings.setBoardEditorFillerEnabled(!ChanSettings.getBoardEditorFillerEnabled()); - item.setChecked(ChanSettings.getBoardEditorFillerEnabled()); - return true; - case android.R.id.home: - finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void addBoard(String value) { - value = value.trim(); - value = value.replace("/", ""); - value = value.replace("\\", ""); // what are you doing?! - - // Duplicate - for (Board board : list) { - if (board.value.equals(value)) { - Toast.makeText(this, R.string.board_add_duplicate, Toast.LENGTH_LONG).show(); - - return; - } - } - - // Normal add - List all = ChanApplication.getBoardManager().getAllBoards(); - for (Board board : all) { - if (board.value.equals(value)) { - board.saved = true; - list.add(board); - adapter.notifyDataSetChanged(); - - Toast.makeText(this, getString(R.string.board_add_success) + " " + board.key, Toast.LENGTH_LONG).show(); - - return; - } - } - - // Unknown - new AlertDialog.Builder(this) - .setTitle(R.string.board_add_unknown_title) - .setMessage(getString(R.string.board_add_unknown, value)) - .setPositiveButton(R.string.ok, null) - .show(); - } - - private void showAddBoardDialog() { - final AutoCompleteTextView text = new AutoCompleteTextView(this); - text.setSingleLine(); - - FillAdapter fillAdapter = new FillAdapter(this, 0); - fillAdapter.setEditingList(list); - fillAdapter.setAutoCompleteView(text); - text.setAdapter(fillAdapter); - text.setThreshold(1); - text.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - text.setHint(R.string.board_add_hint); - text.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); - - AlertDialog dialog = new AlertDialog.Builder(this) - .setPositiveButton(R.string.add, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - String value = text.getText().toString(); - - if (!TextUtils.isEmpty(value)) { - addBoard(value.toLowerCase(Locale.ENGLISH)); - } - } - }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - } - }).setTitle(R.string.board_add).setView(text).create(); - - AndroidUtils.requestKeyboardFocus(dialog, text); - - dialog.show(); - } - - private static class FillAdapter extends ArrayAdapter implements Filterable { - private List currentlyEditing; - private View autoCompleteView; - private final Filter filter; - private final List filtered = new ArrayList<>(); - - public FillAdapter(Context context, int resource) { - super(context, resource); - - filter = new Filter() { - @Override - protected synchronized FilterResults performFiltering(CharSequence constraint) { - FilterResults results = new FilterResults(); - - if (TextUtils.isEmpty(constraint) || (constraint.toString().startsWith(" "))) { - results.values = null; - results.count = 0; - } else { - List keys = getFiltered(constraint.toString()); - results.values = keys; - results.count = keys.size(); - } - - return results; - } - - @SuppressWarnings("unchecked") - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - filtered.clear(); - - if (ChanSettings.getBoardEditorFillerEnabled()) { - if (results.values != null) { - filtered.addAll((List) results.values); - } else { - filtered.addAll(getBoards()); - } - } - - notifyDataSetChanged(); - } - }; - } - - public void setEditingList(List list) { - currentlyEditing = list; - } - - public void setAutoCompleteView(View autoCompleteView) { - this.autoCompleteView = autoCompleteView; - } - - @Override - public int getCount() { - return filtered.size(); - } - - @Override - public String getItem(int position) { - return filtered.get(position).value; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - TextView view = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, null); - Board b = filtered.get(position); - view.setText(b.value + " - " + b.key); - - view.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - InputMethodManager imm = (InputMethodManager) getContext().getSystemService( - Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(autoCompleteView.getWindowToken(), 0); - } - - return false; - } - }); - - return view; - } - - @Override - public Filter getFilter() { - return filter; - } - - private List getFiltered(String filter) { - String lowered = filter.toLowerCase(Locale.ENGLISH); - List list = new ArrayList<>(); - for (Board b : getBoards()) { - if ((b.key.toLowerCase(Locale.ENGLISH).contains(lowered) || b.value.toLowerCase(Locale.ENGLISH) - .contains(lowered))) { - list.add(b); - } - } - return list; - } - - private boolean haveBoard(String value) { - for (Board b : currentlyEditing) { - if (b.value.equals(value)) - return true; - } - return false; - } - - private List getBoards() { - // Lets be cheaty here: if the user has nsfw boards in the list, - // show them in the autofiller. - boolean showUnsafe = false; - for (Board has : currentlyEditing) { - if (!has.workSafe) { - showUnsafe = true; - break; - } - } - - List s = new ArrayList<>(); - for (Board b : ChanApplication.getBoardManager().getAllBoards()) { - if (!haveBoard(b.value) && (showUnsafe || b.workSafe)) - s.add(b); - } - return s; - } - } - - private class NiceDragSortController extends DragSortController { - private final ListView listView; - private final ArrayAdapter adapter; - - public NiceDragSortController(DragSortListView listView, ArrayAdapter adapter) { - super(listView, R.id.drag_handle, DragSortController.ON_DOWN, 0); - this.listView = listView; - this.adapter = adapter; - setSortEnabled(true); - setRemoveEnabled(false); - } - - @Override - public View onCreateFloatView(int position) { - return adapter.getView(position, null, listView); - } - - @Override - public void onDragFloatView(View floatView, Point floatPoint, Point touchPoint) { - } - - @Override - public void onDestroyFloatView(View floatView) { - } - } - - private class BoardEditAdapter extends ArrayAdapter { - public BoardEditAdapter(Context context, int textViewResourceId, List objects) { - super(context, textViewResourceId, objects); - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public long getItemId(int position) { - return getItem(position).hashCode(); - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - View inflated = LayoutInflater.from(getContext()).inflate(R.layout.board_edit_item, null); - TextView text = (TextView) inflated.findViewById(R.id.text); - Board b = getItem(position); - text.setText(b.value + " - " + b.key); - - return inflated; - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ChanActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ChanActivity.java deleted file mode 100644 index 053b4f84..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ChanActivity.java +++ /dev/null @@ -1,759 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.AlertDialog; -import android.app.FragmentTransaction; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.res.Configuration; -import android.net.Uri; -import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarDrawerToggle; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.FrameLayout; -import android.widget.Spinner; -import android.widget.TextView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.loader.ChanLoader; -import org.floens.chan.core.manager.ThreadManager; -import org.floens.chan.core.model.Board; -import org.floens.chan.core.model.ChanThread; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Pin; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.fragment.ThreadFragment; -import org.floens.chan.utils.Logger; - -import java.util.List; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public class ChanActivity extends BaseActivity implements AdapterView.OnItemSelectedListener { - private static final String TAG = "ChanActivity"; - - private Loadable boardLoadable; - private Loadable threadLoadable; - private ThreadFragment boardFragment; - private ThreadFragment threadFragment; - - private boolean ignoreNextOnItemSelected = false; - private Spinner boardSpinner; - private BoardSpinnerAdapter spinnerAdapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - boardLoadable = new Loadable(); - threadLoadable = new Loadable(); - - boardFragment = ThreadFragment.newInstance(this); - setBoardFragmentViewMode(); - - threadFragment = ThreadFragment.newInstance(this); - - FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.replace(R.id.left_pane, boardFragment); - ft.replace(R.id.right_pane, threadFragment); - ft.commitAllowingStateLoss(); - - final ActionBar actionBar = getSupportActionBar(); - - boardSpinner = new Spinner(this); - spinnerAdapter = new BoardSpinnerAdapter(this, boardSpinner); - boardSpinner.setAdapter(spinnerAdapter); - boardSpinner.setOnItemSelectedListener(this); - - actionBar.setCustomView(boardSpinner); - actionBar.setDisplayShowCustomEnabled(true); - - updatePaneState(); - - Intent startIntent = getIntent(); - Uri startUri = startIntent.getData(); - - if (savedInstanceState != null) { - Loadable threadTmp = new Loadable(); - threadTmp.readFromBundle(this, "thread", savedInstanceState); - startLoadingThread(threadTmp); - - // Reset page etc. - Loadable tmp = new Loadable(); - tmp.readFromBundle(this, "board", savedInstanceState); - startLoadingBoard(new Loadable(tmp.board)); - } else { - if (startUri != null) { - handleIntentURI(startUri); - } - - if (boardLoadable.mode == Loadable.Mode.INVALID) { - List savedValues = ChanApplication.getBoardManager().getSavedBoards(); - if (savedValues.size() > 0) { - startLoadingBoard(new Loadable(savedValues.get(0).value)); - } - } - } - - if (startIntent.getExtras() != null) { - handleExtraBundle(startIntent.getExtras()); - } - - ignoreNextOnItemSelected = true; - } - - @Override - protected void onNewIntent(final Intent intent) { - super.onNewIntent(intent); - - if (intent.getExtras() != null) { - handleExtraBundle(intent.getExtras()); - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - boardLoadable.writeToBundle(this, "board", outState); - threadLoadable.writeToBundle(this, "thread", outState); - } - - @Override - protected void onStart() { - super.onStart(); - - ChanApplication.getInstance().activityEnteredForeground(); - } - - @Override - protected void onStop() { - super.onStop(); - - ChanApplication.getInstance().activityEnteredBackground(); - } - - @Override - protected void onPause() { - super.onPause(); - - ChanApplication.getWatchManager().updateDatabase(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - -// ChanApplication.getBoardManager().removeListener(this); - } - - @Override - protected void initDrawer() { - pinDrawerListener = new ActionBarDrawerToggle(this, pinDrawer, R.string.drawer_open, R.string.drawer_close); - - super.initDrawer(); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - pinDrawerListener.syncState(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - pinDrawerListener.onConfigurationChanged(newConfig); - - updatePaneState(); - } - - @Override - public void onBackPressed() { - if (pinDrawer.isDrawerOpen(pinDrawerView)) { - pinDrawer.closeDrawer(pinDrawerView); - } else { - if (threadPane.isOpen()) { - super.onBackPressed(); - } else { - threadPane.openPane(); - } - } - } - - @Override - public void openPin(Pin pin) { - startLoadingThread(pin.loadable); - - pinDrawer.closeDrawer(pinDrawerView); - } - - @Override - public void onOPClicked(Post post) { - Loadable l = new Loadable(post.board, post.no); - l.generateTitle(post); - startLoadingThread(l); - } - - @Override - public void onOpenThread(Loadable thread) { - startLoadingThread(thread); - } - - @Override - public void onThreadLoaded(ChanThread thread) { - updateActionBarState(); - pinnedAdapter.notifyDataSetChanged(); - } - - @Override - public void updatePin(Pin pin) { - super.updatePin(pin); - updateActionBarState(); - } - - @Override - public void removePin(Pin pin) { - super.removePin(pin); - updateActionBarState(); - } - - @Override - public void onNothingSelected(final AdapterView parent) { - } - - @Override - public void onPanelClosed(View view) { - updateActionBarState(); - } - - @Override - public void onPanelOpened(View view) { - updateActionBarState(); - } - -// @Override -// public void onBoardsChanged() { -// spinnerAdapter.setBoards(); -// spinnerAdapter.notifyDataSetChanged(); -// } - - private void handleExtraBundle(Bundle extras) { - int pinId = extras.getInt("pin_id", -2); - if (pinId != -2) { - if (pinId == -1) { - pinDrawer.openDrawer(pinDrawerView); - } else { - Pin pin = ChanApplication.getWatchManager().findPinById(pinId); - if (pin != null) { - startLoadingThread(pin.loadable); - } - } - } - } - - private void updatePaneState() { - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - int width = metrics.widthPixels; - - FrameLayout left = (FrameLayout) findViewById(R.id.left_pane); - FrameLayout right = (FrameLayout) findViewById(R.id.right_pane); - - LayoutParams leftParams = left.getLayoutParams(); - LayoutParams rightParams = right.getLayoutParams(); - - boolean wasSlidable = threadPane.isSlideable(); - boolean isSlidable; - - // Content view dp's: - // Nexus 4 is 384 x 640 dp - // Nexus 7 is 600 x 960 dp - // Nexus 10 is 800 x 1280 dp - - if (ChanSettings.getForcePhoneLayout()) { - leftParams.width = width - dp(30); - rightParams.width = width; - isSlidable = true; - } else { - if (width < dp(400)) { - leftParams.width = width - dp(30); - rightParams.width = width; - isSlidable = true; - } else if (width < dp(800)) { - leftParams.width = width - dp(60); - rightParams.width = width; - isSlidable = true; - } else if (width < dp(1000)) { - leftParams.width = dp(300); - rightParams.width = width - dp(300); - isSlidable = false; - } else { - leftParams.width = dp(400); - rightParams.width = width - dp(400); - isSlidable = false; - } - } - - left.setLayoutParams(leftParams); - right.setLayoutParams(rightParams); - - threadPane.requestLayout(); - left.requestLayout(); - right.requestLayout(); - - LayoutParams drawerParams = pinDrawerView.getLayoutParams(); - - if (width < dp(340)) { - drawerParams.width = dp(280); - } else { - drawerParams.width = dp(320); - } - - pinDrawerView.setLayoutParams(drawerParams); - - updateActionBarState(); - - if (isSlidable != wasSlidable) { - // Terrible hack to sync state for some devices when it changes slidable mode - threadPane.postDelayed(new Runnable() { - @Override - public void run() { - updateActionBarState(); - } - }, 1000); - } - } - - private void updateActionBarState() { - // Force the actionbar state after the ThreadPane layout, - // otherwise the ThreadPane incorrectly reports that it's not slidable. - threadPane.post(new Runnable() { - @Override - public void run() { - updateActionBarStateCallback(); - } - }); - } - - private void updateActionBarStateCallback() { - final ActionBar actionBar = getSupportActionBar(); - - if (threadPane.isSlideable()) { - if (threadPane.isOpen()) { - actionBar.setDisplayShowCustomEnabled(true); - spinnerAdapter.setBoard(boardLoadable.board); - actionBar.setTitle(""); - pinDrawerListener.setDrawerIndicatorEnabled(true); - - if (boardLoadable.isBoardMode()) { - setShareUrl(ChanUrls.getBoardUrlDesktop(boardLoadable.board)); - } else if (boardLoadable.isCatalogMode()) { - setShareUrl(ChanUrls.getCatalogUrlDesktop(boardLoadable.board)); - } - } else { - actionBar.setDisplayShowCustomEnabled(false); - actionBar.setTitle(threadLoadable.title); - pinDrawerListener.setDrawerIndicatorEnabled(false); - - if (threadLoadable.isThreadMode()) - setShareUrl(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no)); - } - } else { - actionBar.setDisplayShowCustomEnabled(true); - pinDrawerListener.setDrawerIndicatorEnabled(true); - actionBar.setTitle(threadLoadable.title); - - if (threadLoadable.isThreadMode()) { - setShareUrl(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no)); - } - } - - actionBar.setHomeButtonEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - - invalidateOptionsMenu(); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - boolean open = threadPane.isOpen(); - boolean slidable = threadPane.isSlideable(); - - setMenuItemEnabled(menu.findItem(R.id.action_reload_board), slidable && open); - setMenuItemEnabled(menu.findItem(R.id.action_reload_thread), slidable && !open); - setMenuItemEnabled(menu.findItem(R.id.action_reload_tablet), !slidable); - - setMenuItemEnabled(menu.findItem(R.id.action_pin), !slidable || !open); - setMenuItemEnabled(menu.findItem(R.id.action_download_album), !slidable || !open); - - setMenuItemEnabled(menu.findItem(R.id.action_reply), slidable); - setMenuItemEnabled(menu.findItem(R.id.action_reply_tablet), !slidable); - - setMenuItemEnabled(menu.findItem(R.id.action_board_view_mode), !slidable || open); - - if (ChanSettings.getBoardViewMode().equals("list")) { - menu.findItem(R.id.action_board_view_mode_list).setChecked(true); - } else if (ChanSettings.getBoardViewMode().equals("grid")) { - menu.findItem(R.id.action_board_view_mode_grid).setChecked(true); - } - - setMenuItemEnabled(menu.findItem(R.id.action_search), slidable); - setMenuItemEnabled(menu.findItem(R.id.action_search_tablet), !slidable); - - boolean bookmarkedFilled = false; - if (threadLoadable.mode == Loadable.Mode.THREAD) { - Pin pin = ChanApplication.getWatchManager().findPinByLoadable(threadLoadable); - if (pin != null) { - bookmarkedFilled = true; - } - } - - menu.findItem(R.id.action_pin).setIcon(bookmarkedFilled ? R.drawable.ic_bookmark_filled : R.drawable.ic_bookmark); - - return super.onPrepareOptionsMenu(menu); - } - - private void setMenuItemEnabled(MenuItem item, boolean enabled) { - if (item != null) { - item.setVisible(enabled); - item.setEnabled(enabled); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (pinDrawerListener.onOptionsItemSelected(item)) { - return true; - } - - switch (item.getItemId()) { - case R.id.action_reload_board: - case R.id.action_reload_tablet_board: - boardFragment.reload(); - return true; - case R.id.action_reload_thread: - case R.id.action_reload_tablet_thread: - threadFragment.reload(); - return true; - case R.id.action_reply: - if (threadPane.isOpen()) { - boardFragment.openReply(); - } else { - threadFragment.openReply(); - } - return true; - case R.id.action_reply_board: - boardFragment.openReply(); - - return true; - case R.id.action_reply_thread: - threadFragment.openReply(); - - return true; - case R.id.action_pin: - if (threadFragment.hasLoader()) { - ChanLoader chanLoader = threadFragment.getLoader(); - if (chanLoader != null && chanLoader.getLoadable().isThreadMode() && chanLoader.getThread() != null) { - Pin pin = ChanApplication.getWatchManager().findPinByLoadable(threadLoadable); - if (pin != null) { - ChanApplication.getWatchManager().removePin(pin); - } else { - ChanApplication.getWatchManager().addPin(chanLoader.getLoadable(), chanLoader.getThread().op); - } - updateActionBarState(); - } - } - - return true; - case R.id.action_open_browser: - openInBrowser(); - - return true; - case R.id.action_board_view_mode_grid: - if (!ChanSettings.getBoardViewMode().equals("grid")) { - ChanSettings.setBoardViewMode("grid"); - setBoardFragmentViewMode(); - startLoadingBoard(boardLoadable); - } - return true; - case R.id.action_board_view_mode_list: - if (!ChanSettings.getBoardViewMode().equals("list")) { - ChanSettings.setBoardViewMode("list"); - setBoardFragmentViewMode(); - startLoadingBoard(boardLoadable); - } - return true; - case R.id.action_search: - if (threadPane.isOpen()) { - boardFragment.startFiltering(); - } else { - threadFragment.startFiltering(); - } - return true; - case R.id.action_search_board: - boardFragment.startFiltering(); - return true; - case R.id.action_search_thread: - threadFragment.startFiltering(); - return true; - case android.R.id.home: - threadPane.openPane(); - - return true; - } - - return super.onOptionsItemSelected(item); - } - - @Override - public void onItemSelected(final AdapterView parent, final View view, final int position, final long id) { - if (ignoreNextOnItemSelected) { - Logger.d(TAG, "Ignoring onItemSelected"); - ignoreNextOnItemSelected = false; - return; - } - - spinnerAdapter.onItemSelected(position); - } - - private void startLoadingBoard(Loadable loadable) { - if (loadable.mode == Loadable.Mode.INVALID) - return; - - boardLoadable = loadable; - - if (ChanSettings.getBoardMode().equals("catalog")) { - boardLoadable.mode = Loadable.Mode.CATALOG; - } else if (ChanSettings.getBoardMode().equals("pages")) { - boardLoadable.mode = Loadable.Mode.BOARD; - } - - // Force catalog mode when using grid - if (boardFragment.getViewMode() == ThreadManager.ViewMode.GRID) { - boardLoadable.mode = Loadable.Mode.CATALOG; - } - - boardFragment.bindLoadable(boardLoadable); - boardFragment.requestData(); - - updateActionBarState(); - } - - private void startLoadingThread(Loadable loadable) { - if (loadable.mode == Loadable.Mode.INVALID) - return; - - Pin pin = ChanApplication.getWatchManager().findPinByLoadable(loadable); - if (pin != null) { - // Use the loadable from the pin. - // This way we can store the listview position in the pin loadable, - // and not in a separate loadable instance. - loadable = pin.loadable; - } - - if (threadLoadable.equals(loadable)) { - threadFragment.requestNextData(); - } else { - threadLoadable = loadable; - threadFragment.bindLoadable(loadable); - threadFragment.requestData(); - } - - threadPane.closePane(); - - updateActionBarState(); - } - - /** - * Handle opening from an external url. - * - * @param startUri - */ - private void handleIntentURI(Uri startUri) { - Logger.d(TAG, "Opening " + startUri.getPath()); - - List parts = startUri.getPathSegments(); - - if (parts.size() == 1) { - // Board mode - String rawBoard = parts.get(0); - if (ChanApplication.getBoardManager().getBoardExists(rawBoard)) { - startLoadingBoard(new Loadable(rawBoard)); - } else { - handleIntentURIFallback(startUri.toString()); - } - } else if (parts.size() >= 3) { - // Thread mode - String rawBoard = parts.get(0); - int no = -1; - - try { - no = Integer.parseInt(parts.get(2)); - } catch (NumberFormatException e) { - } - - int post = -1; - String fragment = startUri.getFragment(); - if (fragment != null) { - int index = fragment.indexOf("p"); - if (index >= 0) { - try { - post = Integer.parseInt(fragment.substring(index + 1)); - } catch (NumberFormatException e) { - } - } - } - - if (no >= 0 && ChanApplication.getBoardManager().getBoardExists(rawBoard)) { - startLoadingThread(new Loadable(rawBoard, no)); - if (post >= 0) { - threadFragment.highlightPost(post); - } - } else { - handleIntentURIFallback(startUri.toString()); - } - } else { - showUrlOpenPicker(startUri.toString()); - } - } - - private void handleIntentURIFallback(final String url) { - new AlertDialog.Builder(this).setTitle(R.string.open_unknown_title).setMessage(R.string.open_unknown) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - showUrlOpenPicker(url); - } - }).setCancelable(false).create().show(); - } - - private void setBoardFragmentViewMode() { - if (ChanSettings.getBoardViewMode().equals("list")) { - boardFragment.setViewMode(ThreadManager.ViewMode.LIST); - } else if (ChanSettings.getBoardViewMode().equals("grid")) { - boardFragment.setViewMode(ThreadManager.ViewMode.GRID); - } - } - - private class BoardSpinnerAdapter extends BaseAdapter { - private Context context; - private Spinner spinner; - private List boards; - private int lastSelectedPosition = 0; - - public BoardSpinnerAdapter(Context context, Spinner spinner) { - this.context = context; - this.spinner = spinner; - setBoards(); - } - - public void setBoards() { - boards = ChanApplication.getBoardManager().getSavedBoards(); - } - - public void setBoard(String boardValue) { - for (int i = 0; i < boards.size(); i++) { - if (boards.get(i).value.equals(boardValue)) { - spinner.setSelection(i); - return; - } - } - } - - public void onItemSelected(int position) { - if (position >= 0 && position < boards.size()) { - Loadable board = new Loadable(boards.get(position).value); - - // onItemSelected is called after the view initializes, - // ignore if it's the same board - if (boardLoadable.equals(board)) - return; - - startLoadingBoard(board); - - lastSelectedPosition = position; - } else { - startActivity(new Intent(context, BoardEditor.class)); - spinner.setSelection(lastSelectedPosition); - } - } - - @Override - public int getCount() { - return boards.size() + 1; - } - - @Override - public long getItemId(final int position) { - return position; - } - - @Override - public String getItem(final int position) { - if (position == getCount() - 1) { - return context.getString(R.string.board_select_add); - } else { - return boards.get(position).key; - } - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - return createView(position, convertView, parent, true); - } - - @Override - public View getView(final int position, View convertView, final ViewGroup parent) { - return createView(position, convertView, parent, false); - } - - private View createView(int position, View convertView, ViewGroup parent, boolean dropDown) { - if (position == getCount() - 1) { - TextView textView = (TextView) LayoutInflater.from(context).inflate(R.layout.board_select_add, parent, false); - textView.setText(getItem(position)); - return textView; - } else { - TextView textView = (TextView) LayoutInflater.from(context).inflate( - dropDown ? R.layout.board_select_spinner_dropdown : R.layout.board_select_spinner, parent, false); - textView.setText(getItem(position)); - return textView; - } - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/DeveloperActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/DeveloperActivity.java deleted file mode 100644 index 39d6c15d..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/DeveloperActivity.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.model.SavedReply; -import org.floens.chan.ui.ThemeActivity; - -import java.util.Random; - -public class DeveloperActivity extends ThemeActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - LinearLayout wrapper = new LinearLayout(this); - wrapper.setOrientation(LinearLayout.VERTICAL); - - Button crashButton = new Button(this); - - crashButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - @SuppressWarnings({"unused", "NumericOverflow"}) - int i = 1 / 0; - } - }); - crashButton.setText("Crash the app"); - - wrapper.addView(crashButton); - - String dbSummary = ""; - - dbSummary += "Database summary:\n"; - dbSummary += ChanApplication.getDatabaseManager().getSummary(); - - TextView db = new TextView(this); - db.setPadding(0, 25, 0, 0); - db.setText(dbSummary); - wrapper.addView(db); - - Button resetDbButton = new Button(this); - resetDbButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ChanApplication.getDatabaseManager().reset(); - System.exit(0); - } - }); - resetDbButton.setText("Delete database"); - wrapper.addView(resetDbButton); - - Button savedReplyDummyAdd = new Button(this); - savedReplyDummyAdd.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - Random r = new Random(); - int j = 0; - for (int i = 0; i < 100; i++) { - j += r.nextInt(10000); - ChanApplication.getDatabaseManager().saveReply(new SavedReply("g", j, "pass")); - } - recreate(); - } - }); - savedReplyDummyAdd.setText("Add test rows to savedReply"); - wrapper.addView(savedReplyDummyAdd); - - Button trimSavedReply = new Button(this); - trimSavedReply.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - ChanApplication.getDatabaseManager().trimSavedRepliesTable(10); - recreate(); - } - }); - trimSavedReply.setText("Trim savedreply table"); - wrapper.addView(trimSavedReply); - - setContentView(wrapper); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImagePickActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImagePickActivity.java index e99e01f5..055eaf1a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImagePickActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImagePickActivity.java @@ -25,7 +25,7 @@ import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.core.reply.ReplyManager; import org.floens.chan.utils.IOUtils; import org.floens.chan.utils.Logger; @@ -53,7 +53,7 @@ public class ImagePickActivity extends Activity implements Runnable { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - replyManager = ChanApplication.getReplyManager(); + replyManager = Chan.getReplyManager(); Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java deleted file mode 100644 index c8d9202c..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ImageViewActivity.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.view.ViewPager; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; -import android.view.View; -import android.widget.ProgressBar; - -import org.floens.chan.R; -import org.floens.chan.chan.ImageSearch; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.manager.ThreadManager; -import org.floens.chan.core.model.Post; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.ui.adapter.ImageViewAdapter; -import org.floens.chan.ui.adapter.PostAdapter; -import org.floens.chan.ui.fragment.ImageViewFragment; -import org.floens.chan.utils.ImageSaver; -import org.floens.chan.utils.Logger; - -import java.util.ArrayList; -import java.util.List; - -/** - * An fragment pager that contains images. Call setPosts first, and then start - * the activity with startActivity() - */ -public class ImageViewActivity extends ThemeActivity implements ViewPager.OnPageChangeListener { - private static final String TAG = "ImageViewActivity"; - - private static PostAdapter postAdapterStatic; - private static int selectedNoStatic = -1; - private static ThreadManager threadManagerStatic; - - private PostAdapter postAdapter; - private ThreadManager threadManager; - private int selectedNo; - - private ImageViewAdapter adapter; - private ViewPager viewPager; - private ProgressBar progressBar; - private int currentPosition; - - /** - * Set the posts to show - * - * @param adapter the adapter to get image data from - * @param selected the no that the user clicked on - */ - public static void launch(Activity activity, PostAdapter adapter, int selected, ThreadManager threadManager) { - postAdapterStatic = adapter; - selectedNoStatic = selected; - threadManagerStatic = threadManager; - - Intent intent = new Intent(activity, ImageViewActivity.class); - activity.startActivity(intent); - activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - if (postAdapterStatic == null || threadManagerStatic == null) { - Logger.e(TAG, "postadapter or threadmanager null"); - finish(); - return; - } - - super.onCreate(savedInstanceState); - - threadManager = threadManagerStatic; - threadManagerStatic = null; - postAdapter = postAdapterStatic; - postAdapterStatic = null; - selectedNo = selectedNoStatic; - selectedNoStatic = -1; - - setContentView(R.layout.image_view); - setToolbar(); - - initProgressBar(); - initPager(); - } - - private void initProgressBar() { - progressBar = (ProgressBar) findViewById(R.id.progress_bar); -// progressBar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_no_bg)); - progressBar.setIndeterminate(false); - progressBar.setMax(1000000); - } - - private void initPager() { - // Get the posts with images - ArrayList imagePosts = new ArrayList<>(); -// for (Post post : postAdapter.getList()) { -// if (post.hasImage) { -// imagePosts.add(post); -// } -// } - - // Setup our pages and adapter - viewPager = (ViewPager) findViewById(R.id.image_pager); - adapter = new ImageViewAdapter(getFragmentManager(), this); - adapter.setList(imagePosts); - viewPager.setAdapter(adapter); - viewPager.setOnPageChangeListener(this); - - // Select the right image - for (int i = 0; i < imagePosts.size(); i++) { - if (imagePosts.get(i).no == selectedNo) { - viewPager.setCurrentItem(i); - onPageSelected(i); - break; - } - } - } - - @Override - protected void onStop() { - super.onStop(); - - // Avoid things like out of sync, since this is an activity. - finish(); - } - - @Override - public void finish() { - super.finish(); - overridePendingTransition(R.anim.fade_in, R.anim.fade_out); - } - - @Override - public void onPageScrollStateChanged(int state) { - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - @Override - public void onPageSelected(int position) { - currentPosition = position; - - for (int i = -1; i <= 1; i++) { - ImageViewFragment fragment = getFragment(position + i); - if (fragment != null) { - fragment.onDeselected(); - } - } - - ImageViewFragment fragment = getFragment(currentPosition); - if (fragment != null) { - fragment.onSelected(adapter, position); - } - - Post post = adapter.getPost(position); - if (!threadManager.arePostRepliesOpen()) { -// postAdapter.scrollToPost(post.no); //TODO - } - } - - public void invalidateActionBar() { - invalidateOptionsMenu(); - } - - public void updateActionBarIfSelected(ImageViewFragment targetFragment) { - ImageViewFragment fragment = getFragment(currentPosition); - if (fragment != null && fragment == targetFragment) { - fragment.onSelected(adapter, currentPosition); - } - } - - public void setProgressBar(long current, long total, boolean done) { - if (done) { - progressBar.setVisibility(View.GONE); - } else { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress((int) (((double) current / total) * progressBar.getMax())); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - case R.id.action_download_album: - if (adapter.getList().size() > 0) { - List list = new ArrayList<>(); - - String folderName = Post.generateTitle(adapter.getList().get(0), 10); - - String filename; - for (Post post : adapter.getList()) { - filename = (ChanSettings.getImageSaveOriginalFilename() ? post.tim : post.filename) + "." + post.ext; - list.add(new ImageSaver.DownloadPair(post.imageUrl, filename)); - } - - ImageSaver.getInstance().saveAll(this, folderName, list); - } - - return true; - default: - ImageViewFragment fragment = getFragment(currentPosition); - if (fragment != null) { - fragment.customOnOptionsItemSelected(item); - } - - return super.onOptionsItemSelected(item); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.image_view, menu); - - MenuItem imageSearch = menu.findItem(R.id.action_image_search); - SubMenu subMenu = imageSearch.getSubMenu(); - for (ImageSearch engine : ImageSearch.engines) { - subMenu.add(Menu.NONE, engine.getId(), Menu.NONE, engine.getName()); - } - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - ImageViewFragment fragment = getFragment(currentPosition); - if (fragment != null) { - fragment.onPrepareOptionsMenu(menu); - } - - return super.onPrepareOptionsMenu(menu); - } - - private ImageViewFragment getFragment(int i) { - if (adapter == null) { - return null; - } else if (i >= 0 && i < adapter.getCount()) { - Object o = adapter.instantiateItem(viewPager, i); - if (o instanceof ImageViewFragment) { - return (ImageViewFragment) o; - } else { - return null; - } - } else { - return null; - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/LicenseActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/LicenseActivity.java deleted file mode 100644 index f427a29f..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/LicenseActivity.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.Activity; -import android.os.Bundle; -import android.webkit.WebView; - -import org.floens.chan.utils.ThemeHelper; - -public class LicenseActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - ThemeHelper.setTheme(this); - - WebView webView = new WebView(this); - webView.loadUrl("file:///android_asset/html/license.html"); - - setContentView(webView); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/PassSettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/PassSettingsActivity.java deleted file mode 100644 index a1770d0b..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/PassSettingsActivity.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.AlertDialog; -import android.app.Fragment; -import android.app.FragmentTransaction; -import android.app.ProgressDialog; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.support.v7.widget.SwitchCompat; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.TextView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.reply.ReplyManager; -import org.floens.chan.core.reply.ReplyManager.PassResponse; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.utils.AndroidUtils; - -public class PassSettingsActivity extends ThemeActivity implements OnCheckedChangeListener { - private SwitchCompat onSwitch; - private TextView toggleStatus; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.header_switch_layout); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - findViewById(R.id.toggle_bar).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onSwitch.toggle(); - } - }); - - toggleStatus = (TextView) findViewById(R.id.toggle_status); - onSwitch = (SwitchCompat) findViewById(R.id.toggle); - onSwitch.setOnCheckedChangeListener(this); - setSwitch(ChanSettings.getPassEnabled()); - - setFragment(ChanSettings.getPassEnabled()); - } - - @Override - public void onPause() { - super.onPause(); - - if (TextUtils.isEmpty(ChanSettings.getPassId())) { - ChanSettings.setPassEnabled(false); - setSwitch(false); - } - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - setFragment(isChecked); - setSwitch(isChecked); - } - - private void setSwitch(boolean enabled) { - onSwitch.setChecked(enabled); - toggleStatus.setText(enabled ? R.string.on : R.string.off); - - ChanSettings.setPassEnabled(enabled); - } - - private void setFragment(boolean enabled) { - if (enabled) { - FragmentTransaction t = getFragmentManager().beginTransaction(); - t.replace(R.id.content, new PassSettingsFragment()); - t.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - t.commit(); - } else { - FragmentTransaction t = getFragmentManager().beginTransaction(); - t.replace(R.id.content, new TextFragment()); - t.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - t.commit(); - } - } - - public static class TextFragment extends Fragment { - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) { - View container = inflater.inflate(R.layout.preference_pass, null); - - TextView link = (TextView) container.findViewById(R.id.pass_link); - link.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AndroidUtils.openLink(v.getContext().getString(R.string.pass_info_link)); - } - }); - - return container; - } - } - - public static class PassSettingsFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preference_pass); - - Preference login = findPreference("preference_pass_login"); - login.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - onLoginClick(ChanSettings.getPassToken(), ChanSettings.getPassPin()); - return true; - } - }); - - updateLoginButton(); - } - - private void updateLoginButton() { - findPreference("preference_pass_login").setTitle(TextUtils.isEmpty(ChanSettings.getPassId()) ? R.string.pass_login : R.string.pass_logout); - } - - private void onLoginClick(String token, String pin) { - if (TextUtils.isEmpty(ChanSettings.getPassId())) { - // Login - final ProgressDialog dialog = ProgressDialog.show(getActivity(), null, "Logging in"); - - ChanApplication.getReplyManager().postPass(token, pin, new ReplyManager.PassListener() { - @Override - public void onResponse(PassResponse response) { - dialog.dismiss(); - - if (getActivity() == null) - return; - - if (response.unknownError) { - WebView webView = new WebView(getActivity()); - WebSettings settings = webView.getSettings(); - settings.setSupportZoom(true); - - webView.loadData(response.responseData, "text/html", null); - - new AlertDialog.Builder(getActivity()).setView(webView).setNeutralButton(R.string.ok, null).show(); - } else { - new AlertDialog.Builder(getActivity()).setMessage(response.message) - .setNeutralButton(R.string.ok, null).show(); - ChanSettings.setPassId(response.passId); - } - - updateLoginButton(); - } - }); - } else { - // Logout - ChanSettings.setPassId(""); - updateLoginButton(); - } - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/ReplyActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/ReplyActivity.java deleted file mode 100644 index 04d43ecf..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/ReplyActivity.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.Fragment; -import android.app.FragmentTransaction; -import android.os.Bundle; -import android.view.MenuItem; - -import org.floens.chan.R; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.ui.fragment.ReplyFragment; -import org.floens.chan.utils.Logger; - -public class ReplyActivity extends ThemeActivity { - private static final String TAG = "ReplyActivity"; - - private static Loadable staticLoadable; - - public static void setLoadable(Loadable l) { - staticLoadable = l; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Loadable loadable = staticLoadable; - staticLoadable = null; - - if (loadable != null && savedInstanceState == null) { - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.replace(R.id.content, ReplyFragment.newInstance(loadable, false), "reply"); - ft.commitAllowingStateLoss(); - } else if (savedInstanceState == null) { - Logger.e(TAG, "Loadable was null, exiting!"); - finish(); - } - } - - @Override - public void onBackPressed() { - Fragment f = getFragmentManager().findFragmentByTag("reply"); - if (f != null && ((ReplyFragment) f).onBackPressed()) { - super.onBackPressed(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - - return true; - } - - return super.onOptionsItemSelected(item); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/SettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/SettingsActivity.java deleted file mode 100644 index 8dd80be0..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/SettingsActivity.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -import org.floens.chan.R; -import org.floens.chan.ui.ThemeActivity; -import org.floens.chan.ui.fragment.SettingsFragment; -import org.floens.chan.utils.ThemeHelper; - -public class SettingsActivity extends ThemeActivity { - private static boolean doingThemeRestart = false; - private static ThemeHelper.Theme lastTheme; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.toolbar_activity); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - if (!doingThemeRestart) { - lastTheme = ThemeHelper.getInstance().getTheme(); - } - - SettingsFragment frag = new SettingsFragment(); - frag.setArguments(getIntent().getExtras()); - getFragmentManager().beginTransaction().replace(R.id.content, frag).commit(); - } - - public void restart(Intent intent) { - doingThemeRestart = true; - startActivity(intent); - finish(); - doingThemeRestart = false; - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } - - @Override - protected void onPause() { - super.onPause(); - - if (ThemeHelper.getInstance().getTheme() != lastTheme) { - lastTheme = ThemeHelper.getInstance().getTheme(); - - BaseActivity.doRestartOnResume = true; - } - } - - @Override - public boolean onCreateOptionsMenu(final Menu menu) { - getMenuInflater().inflate(R.menu.settings, menu); - - return true; - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - if (item.getItemId() == R.id.action_settings_advanced) { - startActivity(new Intent(this, AdvancedSettingsActivity.class)); - return true; - } else if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } else { - return super.onOptionsItemSelected(item); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index a2013473..41789331 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.activity; import android.app.AlertDialog; @@ -7,7 +24,7 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.ViewGroup; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.settings.ChanSettings; @@ -103,21 +120,21 @@ public class StartActivity extends AppCompatActivity { protected void onStart() { super.onStart(); - ChanApplication.getInstance().activityEnteredForeground(); + Chan.getInstance().activityEnteredForeground(); } @Override protected void onStop() { super.onStop(); - ChanApplication.getInstance().activityEnteredBackground(); + Chan.getInstance().activityEnteredBackground(); } @Override protected void onPause() { super.onPause(); - ChanApplication.getWatchManager().updateDatabase(); + Chan.getWatchManager().updateDatabase(); } private Controller stackTop() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java deleted file mode 100644 index 591e889b..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.activity; - -import android.app.Fragment; -import android.app.FragmentTransaction; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceFragment; -import android.support.v7.widget.SwitchCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.TextView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.ThemeActivity; - -public class WatchSettingsActivity extends ThemeActivity implements OnCheckedChangeListener { - private SwitchCompat watchSwitch; - private TextView toggleStatus; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(); - setContentView(R.layout.header_switch_layout); - setToolbar(); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - findViewById(R.id.toggle_bar).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - watchSwitch.toggle(); - } - }); - - toggleStatus = (TextView) findViewById(R.id.toggle_status); - watchSwitch = (SwitchCompat) findViewById(R.id.toggle); - watchSwitch.setOnCheckedChangeListener(this); - setSwitch(ChanSettings.getWatchEnabled()); - - setFragment(ChanSettings.getWatchEnabled()); - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - setFragment(isChecked); - setSwitch(isChecked); - } - - private void setSwitch(boolean enabled) { - watchSwitch.setChecked(enabled); - toggleStatus.setText(enabled ? R.string.on : R.string.off); - - ChanSettings.setWatchEnabled(enabled); - } - - private void setFragment(boolean enabled) { - FragmentTransaction t = getFragmentManager().beginTransaction(); - if (enabled) { - t.replace(R.id.content, new WatchSettingsFragment()); - } else { - t.replace(R.id.content, TextFragment.newInstance(R.string.watch_info_text)); - } - t.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - t.commit(); - } - - public static class TextFragment extends Fragment { - public static TextFragment newInstance(int textResource) { - TextFragment f = new TextFragment(); - Bundle bundle = new Bundle(); - bundle.putInt("text_resource", textResource); - f.setArguments(bundle); - return f; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) { - ViewGroup container = (ViewGroup) inflater.inflate(R.layout.watch_description, null); - - TextView text = (TextView) container.findViewById(R.id.text); - text.setText(getArguments().getInt("text_resource")); - - return container; - } - } - - public static class WatchSettingsFragment extends PreferenceFragment { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preference_watch); - - final ListPreference backgroundTimeout = (ListPreference) findPreference("preference_watch_background_timeout"); - String currentValue = backgroundTimeout.getValue(); - if (currentValue == null) { - backgroundTimeout.setValue((String) backgroundTimeout.getEntryValues()[0]); - currentValue = backgroundTimeout.getValue(); - } - updateListSummary(backgroundTimeout, currentValue); - - backgroundTimeout.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateListSummary(backgroundTimeout, newValue.toString()); - return true; - } - }); - - final ListPreference notifyMode = (ListPreference) findPreference("preference_watch_notify_mode"); - String currentNotifyMode = notifyMode.getValue(); - if (currentNotifyMode == null) { - notifyMode.setValue((String) notifyMode.getEntryValues()[0]); - currentNotifyMode = notifyMode.getValue(); - } - updateListSummary(notifyMode, currentNotifyMode); - - notifyMode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateListSummary(notifyMode, newValue.toString()); - return true; - } - }); - - final ListPreference sound = (ListPreference) findPreference("preference_watch_sound"); - String currentSound = sound.getValue(); - if (currentSound == null) { - sound.setValue((String) sound.getEntryValues()[0]); - currentSound = sound.getValue(); - } - updateListSummary(sound, currentSound); - - sound.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateListSummary(sound, newValue.toString()); - return true; - } - }); - - final ListPreference led = (ListPreference) findPreference("preference_watch_led"); - String currentLed = led.getValue(); - if (currentLed == null) { - led.setValue((String) led.getEntryValues()[0]); - currentLed = led.getValue(); - } - updateListSummary(led, currentLed); - - led.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateListSummary(led, newValue.toString()); - return true; - } - }); - - findPreference("preference_watch_background_enabled").setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, final Object newValue) { - ChanApplication.getWatchManager().onBackgroundWatchingChanged((Boolean) newValue); - - return true; - } - }); - } - - private void updateListSummary(ListPreference backgroundTimeout, String value) { - int index = backgroundTimeout.findIndexOfValue(value); - backgroundTimeout.setSummary(backgroundTimeout.getEntries()[index]); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewAdapter.java deleted file mode 100644 index 09f90d44..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewAdapter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.adapter; - -import android.app.Fragment; -import android.app.FragmentManager; -import android.support.v13.app.FragmentStatePagerAdapter; - -import org.floens.chan.core.model.Post; -import org.floens.chan.ui.activity.ImageViewActivity; -import org.floens.chan.ui.fragment.ImageViewFragment; - -import java.util.ArrayList; -import java.util.List; - -public class ImageViewAdapter extends FragmentStatePagerAdapter { - private final ImageViewActivity activity; - private final ArrayList postList = new ArrayList<>(); - - public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) { - super(fragmentManager); - this.activity = activity; - } - - @Override - public int getCount() { - return postList.size(); - } - - @Override - public Fragment getItem(int position) { - return ImageViewFragment.newInstance(postList.get(position), activity, position); - } - - public Post getPost(int position) { - if (position < 0 || position >= getCount()) - return null; - - return postList.get(position); - } - - public void setList(ArrayList list) { - postList.clear(); - postList.addAll(list); - - notifyDataSetChanged(); - } - - public List getList() { - return postList; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java index 66e314bc..5fb6f653 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.adapter; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java index ab20dde1..9c4e56e0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java @@ -1,6 +1,22 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.adapter; -import android.os.Build; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -8,7 +24,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.core.model.Pin; import org.floens.chan.core.settings.ChanSettings; @@ -21,7 +37,7 @@ import java.util.List; import static org.floens.chan.utils.AndroidUtils.ROBOTO_MEDIUM; import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.getAttrDrawable; +import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; public class PinAdapter extends RecyclerView.Adapter implements SwipeListener.Callback { private static final int PIN_OFFSET = 3; @@ -156,7 +172,7 @@ public class PinAdapter extends RecyclerView.Adapter im @Override public void removeItem(int position) { - ChanApplication.getWatchManager().removePin(pins.get(position - PIN_OFFSET)); + Chan.getWatchManager().removePin(pins.get(position - PIN_OFFSET)); } @Override @@ -240,11 +256,7 @@ public class PinAdapter extends RecyclerView.Adapter im watchCountText = (TextView) itemView.findViewById(R.id.watch_count); watchCountText.setTypeface(ROBOTO_MEDIUM); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - watchCountText.setBackground(getAttrDrawable(itemView.getContext(), android.R.attr.selectableItemBackgroundBorderless)); - } else { - watchCountText.setBackgroundResource(R.drawable.item_background); - } + setRoundItemBackground(watchCountText); itemView.setOnClickListener(new View.OnClickListener() { @Override @@ -271,11 +283,7 @@ public class PinAdapter extends RecyclerView.Adapter im text = (TextView) itemView.findViewById(R.id.text); text.setTypeface(ROBOTO_MEDIUM); image = (ImageView) itemView.findViewById(R.id.image); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - image.setBackground(getAttrDrawable(itemView.getContext(), android.R.attr.selectableItemBackgroundBorderless)); - } else { - image.setBackgroundResource(R.drawable.item_background); - } + setRoundItemBackground(image); image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java deleted file mode 100644 index ad464dd1..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.adapter; - -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.model.Pin; -import org.floens.chan.ui.view.CustomNetworkImageView; - -import java.util.ArrayList; -import java.util.List; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public class PinnedAdapter extends BaseAdapter { - private final static int VIEW_TYPE_ITEM = 0; - private final static int VIEW_TYPE_HEADER = 1; - - private Context context; - private ListView listView; - private List pins = new ArrayList<>(); - private boolean postInvalidated = false; - - public PinnedAdapter(Context context, ListView listView) { - this.context = context; - this.listView = listView; - } - - @Override - public int getCount() { - return pins.size() + 1; - } - - @Override - public int getViewTypeCount() { - return 2; - } - - @Override - public int getItemViewType(final int position) { - return position == 0 ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM; - } - - @Override - public Pin getItem(final int position) { - switch (getItemViewType(position)) { - case VIEW_TYPE_ITEM: - int itemPosition = position - 1; - if (itemPosition >= 0 && itemPosition < pins.size()) { - return pins.get(itemPosition); - } else { - return null; - } - case VIEW_TYPE_HEADER: - return null; - default: - return null; - } - } - - @Override - public long getItemId(int position) { - switch (getItemViewType(position)) { - case VIEW_TYPE_ITEM: - int itemPosition = position - 1; - if (itemPosition >= 0 && itemPosition < pins.size()) { - return pins.get(itemPosition).id; - } else { - return -1; - } - case VIEW_TYPE_HEADER: - return -1; - default: - return -1; - } - } - - @Override - public boolean hasStableIds() { - return true; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - switch (getItemViewType(position)) { - case VIEW_TYPE_ITEM: { - final Pin pin = getItem(position); - - if (convertView == null) { - convertView = LayoutInflater.from(context).inflate(R.layout.pin_item, null); - } - - CustomNetworkImageView imageView = (CustomNetworkImageView) convertView.findViewById(R.id.image); - if (pin.thumbnailUrl != null) { - imageView.setVisibility(View.VISIBLE); - imageView.setFadeIn(0); - imageView.forceImageDimensions(dp(48), dp(48)); - imageView.setImageUrl(pin.thumbnailUrl, ChanApplication.getVolleyImageLoader()); - } else { - imageView.setVisibility(View.GONE); - } - - ((TextView) convertView.findViewById(R.id.text)).setText(pin.loadable.title); - - FrameLayout timeContainer = (FrameLayout) convertView.findViewById(R.id.time_container); - FrameLayout countContainer = (FrameLayout) convertView.findViewById(R.id.pin_count_container); - if (ChanSettings.getWatchEnabled()) { - countContainer.setVisibility(View.VISIBLE); - - TextView timeView = (TextView) convertView.findViewById(R.id.time); - if (pin.watching && pin.getPinWatcher() != null && ChanSettings.getWatchCountdownVisibleEnabled()) { - timeContainer.setVisibility(View.VISIBLE); - long timeRaw = pin.getPinWatcher().getTimeUntilNextLoad(); - long time = 0; - if (timeRaw > 0) { - time = timeRaw / 1000L; - time = Math.min(9999, time); - } - - timeView.setText(Long.toString(time)); - - postInvalidate(); - } else { - timeContainer.setVisibility(View.GONE); - } - - TextView countView = (TextView) convertView.findViewById(R.id.pin_count); - ProgressBar loadView = (ProgressBar) convertView.findViewById(R.id.pin_load); - - if (pin.isError) { - countView.setText("Err"); - } else { - int count = pin.getNewPostCount(); - String total = Integer.toString(count); - if (count > 999) { - total = "1k+"; - } - countView.setText(total); - } - - if (pin.getPinWatcher() != null && pin.getPinWatcher().isLoading()) { - countView.setVisibility(View.GONE); - loadView.setVisibility(View.VISIBLE); - } else { - loadView.setVisibility(View.GONE); - countView.setVisibility(View.VISIBLE); - } - - countContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { -// pin.toggleWatch(); - } - }); - - if (!pin.watching) { - countContainer.setBackgroundResource(R.drawable.pin_icon_gray); - } else if (pin.getNewQuoteCount() > 0) { - countContainer.setBackgroundResource(R.drawable.pin_icon_red); - } else { - countContainer.setBackgroundResource(R.drawable.pin_icon_blue); - } - } else { - timeContainer.setVisibility(View.GONE); - countContainer.setVisibility(View.GONE); - } - - return convertView; - } - case VIEW_TYPE_HEADER: { - if (convertView == null) { - convertView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pin_item_header, null); - ((TextView) convertView.findViewById(R.id.pin_header)).setText(R.string.drawer_pinned); - } - - return convertView; - } - default: - return null; - } - } - - public void reload() { - pins.clear(); - pins.addAll(ChanApplication.getWatchManager().getPins()); - - notifyDataSetChanged(); - } - - private void postInvalidate() { - if (!postInvalidated) { - postInvalidated = true; - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - postInvalidated = false; - listView.invalidateViews(); - } - }, 1000); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/animation/ScrollerRunnable.java b/Clover/app/src/main/java/org/floens/chan/ui/animation/ScrollerRunnable.java deleted file mode 100644 index 3e984f6e..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/animation/ScrollerRunnable.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.animation; - -import android.view.View; -import android.view.ViewConfiguration; -import android.widget.AbsListView; -import android.widget.ListView; - -public class ScrollerRunnable implements Runnable { - private static final int SCROLL_DURATION = 300; - - private static final int MOVE_DOWN_POS = 1; - private static final int MOVE_UP_POS = 2; - - private final AbsListView mList; - - private int mMode; - private int mTargetPos; - private int mLastSeenPos; - private int mScrollDuration; - private final int mExtraScroll; - - public ScrollerRunnable(AbsListView listView) { - mList = listView; - mExtraScroll = ViewConfiguration.get(mList.getContext()).getScaledFadingEdgeLength(); - } - - public void start(int position) { - stop(); - - final int firstPos = mList.getFirstVisiblePosition(); - final int lastPos = firstPos + mList.getChildCount() - 1; - - int viewTravelCount; - if (position <= firstPos) { - viewTravelCount = firstPos - position + 1; - mMode = MOVE_UP_POS; - } else if (position >= lastPos) { - viewTravelCount = position - lastPos + 1; - mMode = MOVE_DOWN_POS; - } else { - // Already on screen, nothing to do - return; - } - - if (viewTravelCount > 0) { - mScrollDuration = SCROLL_DURATION / viewTravelCount; - } else { - mScrollDuration = SCROLL_DURATION; - } - mTargetPos = position; - mLastSeenPos = ListView.INVALID_POSITION; - - mList.post(this); - } - - void stop() { - mList.removeCallbacks(this); - } - - @Override - public void run() { - final int listHeight = mList.getHeight(); - final int firstPos = mList.getFirstVisiblePosition(); - - switch (mMode) { - case MOVE_DOWN_POS: { - final int lastViewIndex = mList.getChildCount() - 1; - final int lastPos = firstPos + lastViewIndex; - - if (lastViewIndex < 0) { - return; - } - - if (lastPos == mLastSeenPos) { - // No new views, let things keep going. - mList.post(this); - return; - } - - final View lastView = mList.getChildAt(lastViewIndex); - final int lastViewHeight = lastView.getHeight(); - final int lastViewTop = lastView.getTop(); - final int lastViewPixelsShowing = listHeight - lastViewTop; - final int extraScroll = lastPos < mList.getCount() - 1 ? mExtraScroll : mList.getPaddingBottom(); - - mList.smoothScrollBy(lastViewHeight - lastViewPixelsShowing + extraScroll, mScrollDuration); - - mLastSeenPos = lastPos; - if (lastPos < mTargetPos) { - mList.post(this); - } - break; - } - - case MOVE_UP_POS: { - if (firstPos == mLastSeenPos) { - // No new views, let things keep going. - mList.post(this); - return; - } - - final View firstView = mList.getChildAt(0); - if (firstView == null) { - return; - } - final int firstViewTop = firstView.getTop(); - final int extraScroll = firstPos > 0 ? mExtraScroll : mList.getPaddingTop(); - - mList.smoothScrollBy(firstViewTop - extraScroll, mScrollDuration); - - mLastSeenPos = firstPos; - - if (firstPos > mTargetPos) { - mList.post(this); - } - break; - } - - default: - break; - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/animation/SwipeDismissListViewTouchListener.java b/Clover/app/src/main/java/org/floens/chan/ui/animation/SwipeDismissListViewTouchListener.java deleted file mode 100644 index 367d1e1f..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/animation/SwipeDismissListViewTouchListener.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * 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.floens.chan.ui.animation; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.graphics.Rect; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.ListView; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A {@link android.view.View.OnTouchListener} that makes the list items in a - * {@link ListView} dismissable. {@link ListView} is given special treatment - * because by default it handles touches for its list items... i.e. it's in - * charge of drawing the pressed state (the list selector), handling list item - * clicks, etc. - *

- *

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

- *

- *

- * Example usage: - *

- *

- *

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

- *

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

- */ -public class SwipeDismissListViewTouchListener implements View.OnTouchListener { - // Cached ViewConfiguration and system-wide constant values - private int mSlop; - private final int mMinFlingVelocity; - private final int mMaxFlingVelocity; - private final long mAnimationTime; - - // Fixed properties - private final ListView mListView; - private final DismissCallbacks mCallbacks; - private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero - - // Transient properties - private final List mPendingDismisses = new ArrayList<>(); - private int mDismissAnimationRefCount = 0; - private float mDownX; - private boolean mSwiping; - 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; - } - - public int getSlop() { - return mSlop; - } - - public void setSlop(int slop) { - mSlop = slop; - } - - /** - * 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 android.widget.AbsListView.OnScrollListener} to be - * added to the {@link ListView} using - * {@link ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)} - * . If a scroll listener is already assigned, the caller should still pass - * scroll changes through to this listener. This will ensure that this - * {@link SwipeDismissListViewTouchListener} is paused during list view - * scrolling.

- * - * @see SwipeDismissListViewTouchListener - */ - public AbsListView.OnScrollListener makeScrollListener() { - return new AbsListView.OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView absListView, int scrollState) { - setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); - } - - @Override - public void onScroll(AbsListView absListView, int i, int i1, int i2) { - } - }; - } - - /** - * Manually cause the item at the given position to be dismissed (trigger - * the dismiss animation). - */ - public void dismiss(int position) { - dismiss(getViewForPosition(position), position, true); - } - - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - if (mViewWidth < 2) { - mViewWidth = mListView.getWidth(); - } - - mListView.requestDisallowInterceptTouchEvent(true); - - 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(); - mDownPosition = mListView.getPositionForView(mDownView); - if (mCallbacks.canDismiss(mDownPosition)) { - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.addMovement(motionEvent); - } else { - mDownView = null; - } - } - view.onTouchEvent(motionEvent); - return true; - } - - 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) { - dismiss = true; - dismissRight = deltaX > 0; - } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity - && absVelocityY < absVelocityX) { - // dismiss only if flinging in the same direction as dragging - dismiss = (velocityX < 0) == (deltaX < 0); - dismissRight = mVelocityTracker.getXVelocity() > 0; - } - if (dismiss) { - // dismiss - dismiss(mDownView, mDownPosition, dismissRight); - } else { - // cancel - mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null); - } - mVelocityTracker.recycle(); - mVelocityTracker = null; - mDownX = 0; - mDownView = null; - mDownPosition = ListView.INVALID_POSITION; - mSwiping = false; - break; - } - - case MotionEvent.ACTION_CANCEL: { - if (mVelocityTracker == null) { - break; - } - - if (mDownView != null) { - // cancel - mDownView.animate().translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null); - } - mVelocityTracker.recycle(); - mVelocityTracker = null; - mDownX = 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; - if (Math.abs(deltaX) > mSlop) { - mSwiping = true; - 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); - mDownView.setAlpha(Math.max(0.15f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth))); - return true; - } - break; - } - } - return false; - } - - private void dismiss(final View view, final int position, boolean dismissRight) { - ++mDismissAnimationRefCount; - if (view == null) { - // No view, shortcut to calling onDismiss to let it deal with adapter - // updates and all that. - mCallbacks.onDismiss(mListView, new int[]{position}); - return; - } - - view.animate().translationX(dismissRight ? mViewWidth : -mViewWidth).alpha(0).setDuration(mAnimationTime) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - performDismiss(view, position); - } - }); - } - - private View getViewForPosition(int position) { - int index = position - (mListView.getFirstVisiblePosition() - mListView.getHeaderViewsCount()); - return (index >= 0 && index < mListView.getChildCount()) ? mListView.getChildAt(index) : null; - } - - class PendingDismissData implements Comparable { - public int position; - public View view; - - public PendingDismissData(int position, View view) { - this.position = position; - this.view = view; - } - - @Override - public int compareTo(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); - - 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); - } - - 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(); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/animation/ViewFlipperAnimations.java b/Clover/app/src/main/java/org/floens/chan/ui/animation/ViewFlipperAnimations.java deleted file mode 100644 index 54e7a0cf..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/animation/ViewFlipperAnimations.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.animation; - -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.TranslateAnimation; - -/** - * Contains the TranslateAnimation's for a horizontal scrolling ViewFlipper. - */ -public class ViewFlipperAnimations { - public static TranslateAnimation BACK_IN; - public static TranslateAnimation BACK_OUT; - public static TranslateAnimation NEXT_IN; - public static TranslateAnimation NEXT_OUT; - - static { - // Setup the static TranslateAnimations for the ViewFlipper - BACK_IN = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, -1f, Animation.RELATIVE_TO_PARENT, 0f, 0, 0f, 0, - 0f); - BACK_IN.setInterpolator(new AccelerateDecelerateInterpolator()); - BACK_IN.setDuration(300); - - BACK_OUT = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 1f, 0, 0f, 0, - 0f); - BACK_OUT.setInterpolator(new AccelerateDecelerateInterpolator()); - BACK_OUT.setDuration(300); - - NEXT_IN = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 1f, Animation.RELATIVE_TO_PARENT, 0f, 0, 0f, 0, - 0f); - NEXT_IN.setInterpolator(new AccelerateDecelerateInterpolator()); - NEXT_IN.setDuration(300); - - NEXT_OUT = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, -1f, 0, 0f, - 0, 0f); - NEXT_OUT.setInterpolator(new AccelerateDecelerateInterpolator()); - NEXT_OUT.setDuration(300); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java index bcfa5d8e..2780f159 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java @@ -1,10 +1,26 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.cell; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; -import android.os.Build; import android.support.annotation.NonNull; import android.text.Layout; import android.text.Spannable; @@ -29,7 +45,7 @@ import android.widget.TextView; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; @@ -46,8 +62,8 @@ import java.util.ArrayList; import java.util.List; import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.getAttrDrawable; import static org.floens.chan.utils.AndroidUtils.getRes; +import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; import static org.floens.chan.utils.AndroidUtils.sp; public class PostCell extends RelativeLayout implements PostLinkable.Callback { @@ -132,17 +148,9 @@ public class PostCell extends RelativeLayout implements PostLinkable.Callback { replies.setTextSize(textSizeSp); replies.setPadding(paddingPx, 0, paddingPx, paddingPx); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - replies.setBackground(getAttrDrawable(getContext(), android.R.attr.selectableItemBackgroundBorderless)); - } else { - replies.setBackgroundResource(R.drawable.item_background); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - options.setBackground(getAttrDrawable(getContext(), android.R.attr.selectableItemBackgroundBorderless)); - } else { - options.setBackgroundResource(R.drawable.item_background); - } + setRoundItemBackground(replies); + setRoundItemBackground(options); RelativeLayout.LayoutParams dividerParams = (LayoutParams) divider.getLayoutParams(); dividerParams.leftMargin = paddingPx; @@ -382,7 +390,7 @@ public class PostCell extends RelativeLayout implements PostLinkable.Callback { private void loadCountryIcon() { final Post requestedPost = post; - ChanApplication.getVolleyImageLoader().get(post.countryUrl, new ImageLoader.ImageListener() { + Chan.getVolleyImageLoader().get(post.countryUrl, new ImageLoader.ImageListener() { @Override public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) { if (response.getBitmap() != null && PostCell.this.post == requestedPost) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/cell/ThreadStatusCell.java b/Clover/app/src/main/java/org/floens/chan/ui/cell/ThreadStatusCell.java index 162879df..a1172287 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/cell/ThreadStatusCell.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/cell/ThreadStatusCell.java @@ -1,7 +1,23 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.cell; import android.content.Context; -import android.os.Build; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; @@ -14,7 +30,6 @@ import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Post; import static org.floens.chan.utils.AndroidUtils.ROBOTO_MEDIUM; -import static org.floens.chan.utils.AndroidUtils.getAttrDrawable; public class ThreadStatusCell extends LinearLayout implements View.OnClickListener { private static final int UPDATE_INTERVAL = 1000; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/AdvancedSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/AdvancedSettingsController.java index f0b861a4..38c880bb 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/AdvancedSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/AdvancedSettingsController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.app.Activity; @@ -42,10 +59,11 @@ public class AdvancedSettingsController extends SettingsController { private void populatePreferences() { SettingsGroup settings = new SettingsGroup(string(R.string.settings_group_advanced)); + // TODO change this to a presenting controller saveLocation = (LinkSettingView) settings.add(new LinkSettingView(this, string(R.string.setting_save_folder), null, new View.OnClickListener() { @Override public void onClick(View v) { - File dir = ChanSettings.getImageSaveDirectory(); + File dir = new File(ChanSettings.saveLocation.get()); if (!dir.mkdirs() && !dir.isDirectory()) { new AlertDialog.Builder(context).setMessage(R.string.setting_save_folder_error_create_folder).show(); } else { @@ -66,8 +84,8 @@ public class AdvancedSettingsController extends SettingsController { settings.add(new BooleanSettingView(this, ChanSettings.shareUrl, string(R.string.setting_share_url), string(R.string.setting_share_url_description))); settings.add(new BooleanSettingView(this, ChanSettings.networkHttps, string(R.string.setting_network_https), string(R.string.setting_network_https_description))); settings.add(new BooleanSettingView(this, ChanSettings.forcePhoneLayout, string(R.string.setting_force_phone_layout), null)); - settings.add(new BooleanSettingView(this, ChanSettings.anonymize, string(R.string.preference_anonymize), null)); - settings.add(new BooleanSettingView(this, ChanSettings.anonymizeIds, string(R.string.preference_anonymize_ids), null)); + settings.add(new BooleanSettingView(this, ChanSettings.anonymize, string(R.string.setting_anonymize), null)); + settings.add(new BooleanSettingView(this, ChanSettings.anonymizeIds, string(R.string.setting_anonymize_ids), null)); settings.add(new BooleanSettingView(this, ChanSettings.repliesButtonsBottom, string(R.string.setting_buttons_bottom), null)); settings.add(new BooleanSettingView(this, ChanSettings.confirmExit, string(R.string.setting_confirm_exit), null)); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java index fdd7a4b8..b0c0f310 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardEditController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.app.AlertDialog; @@ -19,7 +36,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.manager.BoardManager; @@ -40,7 +57,7 @@ import static org.floens.chan.utils.AndroidUtils.dp; public class BoardEditController extends Controller implements SwipeListener.Callback, ToolbarMenuItem.ToolbarMenuItemCallback { private static final int ADD_ID = 1; - private final BoardManager boardManager = ChanApplication.getBoardManager(); + private final BoardManager boardManager = Chan.getBoardManager(); private RecyclerView recyclerView; private BoardEditAdapter adapter; @@ -173,7 +190,7 @@ public class BoardEditController extends Controller implements SwipeListener.Cal } // Normal add - List all = ChanApplication.getBoardManager().getAllBoards(); + List all = Chan.getBoardManager().getAllBoards(); for (Board board : all) { if (board.value.equals(value)) { board.saved = true; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index 1fa42095..a4b164c4 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -25,7 +25,7 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.manager.BoardManager; @@ -68,8 +68,8 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte navigationItem.menu = menu; navigationItem.hasBack = false; - menu.addItem(new ToolbarMenuItem(context, this, REFRESH_ID, R.drawable.ic_action_refresh)); - menu.addItem(new ToolbarMenuItem(context, this, POST_ID, R.drawable.ic_action_write)); + menu.addItem(new ToolbarMenuItem(context, this, REFRESH_ID, R.drawable.ic_refresh_white_24dp)); + menu.addItem(new ToolbarMenuItem(context, this, POST_ID, R.drawable.ic_create_white_24dp)); ToolbarMenuItem overflow = menu.createOverflow(this); @@ -80,7 +80,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items)); - loadBoard(ChanApplication.getBoardManager().getSavedBoards().get(0)); + loadBoard(Chan.getBoardManager().getSavedBoards().get(0)); } @Override @@ -168,7 +168,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte } private void loadBoards() { - List boards = ChanApplication.getBoardManager().getSavedBoards(); + List boards = Chan.getBoardManager().getSavedBoards(); boardItems = new ArrayList<>(); for (Board board : boards) { FloatingMenuItem item = new FloatingMenuItemBoard(board); @@ -222,7 +222,7 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte if (position >= 0 && position < items.size()) { return items.get(position).getText(); } else { - return context.getString(R.string.board_select_add); + return context.getString(R.string.thread_board_select_add); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java index 2b4e27e6..6164e112 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/DeveloperSettingsController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.content.Context; @@ -7,7 +24,7 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.model.SavedReply; @@ -53,7 +70,7 @@ public class DeveloperSettingsController extends Controller { resetDbButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ChanApplication.getDatabaseManager().reset(); + Chan.getDatabaseManager().reset(); System.exit(0); } }); @@ -68,7 +85,7 @@ public class DeveloperSettingsController extends Controller { int j = 0; for (int i = 0; i < 100; i++) { j += r.nextInt(10000); - ChanApplication.getDatabaseManager().saveReply(new SavedReply("g", j, "pass")); + Chan.getDatabaseManager().saveReply(new SavedReply("g", j, "pass")); } setDbSummary(); } @@ -80,7 +97,7 @@ public class DeveloperSettingsController extends Controller { trimSavedReply.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { - ChanApplication.getDatabaseManager().trimSavedRepliesTable(10); + Chan.getDatabaseManager().trimSavedRepliesTable(10); setDbSummary(); } }); @@ -95,7 +112,7 @@ public class DeveloperSettingsController extends Controller { private void setDbSummary() { String dbSummary = ""; dbSummary += "Database summary:\n"; - dbSummary += ChanApplication.getDatabaseManager().getSummary(); + dbSummary += Chan.getDatabaseManager().getSummary(); summaryText.setText(dbSummary); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java index 651e01ed..e7db17f7 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.animation.Animator; @@ -5,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; @@ -28,7 +46,7 @@ import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import com.davemorrissey.labs.subscaleview.ImageViewState; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ImageSearch; import org.floens.chan.controller.Controller; @@ -249,6 +267,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis if (ChanSettings.videoErrorIgnore.get()) { Toast.makeText(context, R.string.image_open_failed, Toast.LENGTH_SHORT).show(); } else { + @SuppressLint("InflateParams") View notice = LayoutInflater.from(context).inflate(R.layout.dialog_video_error, null); final CheckBox dontShowAgain = (CheckBox) notice.findViewById(R.id.checkbox); @@ -309,7 +328,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis } }); - ChanApplication.getVolleyImageLoader().get(postImage.thumbnailUrl, new ImageLoader.ImageListener() { + Chan.getVolleyImageLoader().get(postImage.thumbnailUrl, new ImageLoader.ImageListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, "onErrorResponse for preview in transition in ImageViewerController, cannot show correct transition bitmap"); @@ -331,7 +350,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis return; } - ChanApplication.getVolleyImageLoader().get(postImage.thumbnailUrl, new ImageLoader.ImageListener() { + Chan.getVolleyImageLoader().get(postImage.thumbnailUrl, new ImageLoader.ImageListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, "onErrorResponse for preview out transition in ImageViewerController, cannot show correct transition bitmap"); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java index 0e89789a..d9008412 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerNavigationController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/LicensesController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/LicensesController.java index 257864f6..baaa1e58 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/LicensesController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/LicensesController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java index d0432671..6ffcec71 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.app.AlertDialog; @@ -12,7 +29,7 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.core.reply.ReplyManager; @@ -100,7 +117,7 @@ public class PassSettingsController extends Controller implements View.OnClickLi ChanSettings.passToken.set(inputToken.getText().toString()); ChanSettings.passPin.set(inputPin.getText().toString()); - ChanApplication.getReplyManager().postPass(ChanSettings.passToken.get(), ChanSettings.passPin.get(), new ReplyManager.PassListener() { + Chan.getReplyManager().postPass(ChanSettings.passToken.get(), ChanSettings.passPin.get(), new ReplyManager.PassListener() { @Override public void onResponse(ReplyManager.PassResponse response) { if (response.isError) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java index 896d652d..bdf7a4f1 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PostRepliesController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.animation.ValueAnimator; @@ -24,7 +41,6 @@ import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.cell.PostCell; import org.floens.chan.ui.helper.PostPopupHelper; import org.floens.chan.ui.view.LoadView; -import org.floens.chan.ui.view.PostView; import org.floens.chan.ui.view.ThumbnailView; import org.floens.chan.utils.ThemeHelper; @@ -133,8 +149,8 @@ public class PostRepliesController extends Controller { }); if (!ThemeHelper.getInstance().getTheme().isLightTheme) { - ((TextView) dataView.findViewById(R.id.replies_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_back_dark, 0, 0, 0); - ((TextView) dataView.findViewById(R.id.replies_close_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_done_dark, 0, 0, 0); + ((TextView) dataView.findViewById(R.id.replies_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_arrow_back_white_24dp, 0, 0, 0); + ((TextView) dataView.findViewById(R.id.replies_close_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_done_white_24dp, 0, 0, 0); dataView.findViewById(R.id.container).setBackgroundResource(R.drawable.dialog_full_dark); } @@ -142,24 +158,15 @@ public class PostRepliesController extends Controller { @Override public View getView(int position, View convertView, ViewGroup parent) { PostCell postCell; - if (convertView instanceof PostView) { + if (convertView instanceof PostCell) { postCell = (PostCell) convertView; } else { - postCell = (PostCell) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_post, parent, false); + postCell = (PostCell) LayoutInflater.from(parent.getContext()).inflate(R.layout.cell_post, parent, false); } final Post p = getItem(position); postCell.setPost(p, presenter, false, data.forPost.no); -// postView.setPost(p, presenter, false); -// postView.setHighlightQuotesWithNo(data.forPost.no); - /*postCell.setOnClickListeners(new View.OnClickListener() { - @Override - public void onClick(View v) { - postPopupHelper.postClicked(p); - } - });*/ - return postCell; } }; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java index f4d1e67b..21c9d13e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java @@ -25,7 +25,7 @@ import android.view.Gravity; import android.view.View; import android.widget.FrameLayout; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.controller.ControllerTransition; @@ -72,7 +72,7 @@ public class RootNavigationController extends NavigationController implements Pi new SwipeListener(context, recyclerView, pinAdapter); - pinAdapter.onPinsChanged(ChanApplication.getWatchManager().getPins()); + pinAdapter.onPinsChanged(Chan.getWatchManager().getPins()); toolbar.setCallback(this); @@ -162,7 +162,7 @@ public class RootNavigationController extends NavigationController implements Pi @Override public void onWatchCountClicked(Pin pin) { - ChanApplication.getWatchManager().toggleWatch(pin); + Chan.getWatchManager().toggleWatch(pin); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java index 1813a228..245c8662 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java @@ -1,8 +1,25 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.content.Context; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.controller.Controller; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.PostImage; @@ -45,7 +62,7 @@ public abstract class ThreadController extends Controller implements ThreadLayou return threadLayout.onBack(); } - public void onEvent(ChanApplication.ForegroundChangedMessage message) { + public void onEvent(Chan.ForegroundChangedMessage message) { threadLayout.getPresenter().onForegroundChanged(message.inForeground); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java index 32ba1bcc..81c50347 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java @@ -21,7 +21,7 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.manager.WatchManager; @@ -62,8 +62,8 @@ public class ViewThreadController extends ThreadController implements ThreadLayo navigationItem.hasDrawer = true; navigationItem.menu = new ToolbarMenu(context); - navigationItem.menu.addItem(new ToolbarMenuItem(context, this, POST_ID, R.drawable.ic_action_write)); - pinItem = navigationItem.menu.addItem(new ToolbarMenuItem(context, this, PIN_ID, R.drawable.ic_bookmark)); + navigationItem.menu.addItem(new ToolbarMenuItem(context, this, POST_ID, R.drawable.ic_create_white_24dp)); + pinItem = navigationItem.menu.addItem(new ToolbarMenuItem(context, this, PIN_ID, R.drawable.ic_bookmark_outline_white_24dp)); navigationItem.createOverflow(context, this, Arrays.asList( new FloatingMenuItem(REFRESH_ID, context.getString(R.string.action_reload)), new FloatingMenuItem(SEARCH_ID, context.getString(R.string.action_search)), @@ -177,11 +177,11 @@ public class ViewThreadController extends ThreadController implements ThreadLayo } private void setPinIconState() { - WatchManager wm = ChanApplication.getWatchManager(); + WatchManager wm = Chan.getWatchManager(); setPinIconState(wm.findPinByLoadable(loadable) != null); } private void setPinIconState(boolean pinned) { - pinItem.setImage(pinned ? R.drawable.ic_bookmark_filled : R.drawable.ic_bookmark); + pinItem.setImage(pinned ? R.drawable.ic_bookmark_white_24dp : R.drawable.ic_bookmark_outline_white_24dp); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java index 7592c710..3398f629 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/WatchSettingsController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.controller; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java b/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java index 37e05f8b..49a5f1cb 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/drawable/DropdownArrowDrawable.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.drawable; import android.graphics.Canvas; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java b/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java index 5f6c1031..1e97c808 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/drawable/ThumbDrawable.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.drawable; import android.graphics.Canvas; @@ -9,7 +26,6 @@ import android.graphics.drawable.Drawable; import static org.floens.chan.utils.AndroidUtils.dp; - public class ThumbDrawable extends Drawable { private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Path path = new Path(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java index 1e02bd84..2f7dbe0f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/fragment/FolderPickFragment.java @@ -71,7 +71,7 @@ public class FolderPickFragment extends DialogFragment { return null; } - View container = inflater.inflate(R.layout.folder_pick, parent); + View container = inflater.inflate(R.layout.fragment_folder_pick, parent); statusPath = (TextView) container.findViewById(R.id.folder_status); listView = (ListView) container.findViewById(R.id.folder_list); @@ -94,8 +94,8 @@ public class FolderPickFragment extends DialogFragment { }); if (!ThemeHelper.getInstance().getTheme().isLightTheme) { - ((TextView) container.findViewById(R.id.pick_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_cancel_dark, 0, 0, 0); - ((TextView) container.findViewById(R.id.pick_ok_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_done_dark, 0, 0, 0); + ((TextView) container.findViewById(R.id.pick_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_arrow_back_white_24dp, 0, 0, 0); + ((TextView) container.findViewById(R.id.pick_ok_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_done_white_24dp, 0, 0, 0); } adapter = new ArrayAdapter(inflater.getContext(), 0) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java deleted file mode 100644 index 35434b4a..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ImageViewFragment.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.fragment; - -import android.app.AlertDialog; -import android.app.Fragment; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.VideoView; - -import org.floens.chan.R; -import org.floens.chan.chan.ImageSearch; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.activity.ImageViewActivity; -import org.floens.chan.ui.adapter.ImageViewAdapter; -import org.floens.chan.ui.view.MultiImageView; -import org.floens.chan.ui.view.MultiImageView.Callback; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.ImageSaver; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public class ImageViewFragment extends Fragment implements Callback { - private Context context; - private ImageViewActivity activity; - - private MultiImageView imageView; - - private Post post; - private boolean showProgressBar = true; - private boolean isVideo = false; - private boolean videoVisible = false; - private boolean videoSetIconToPause = false; - private boolean tapToLoad = false; - private boolean loaded = false; - - private long progressCurrent; - private long progressTotal; - private boolean progressDone; - - public static ImageViewFragment newInstance(Post post, ImageViewActivity activity, int index) { - ImageViewFragment imageViewFragment = new ImageViewFragment(); - imageViewFragment.post = post; - imageViewFragment.activity = activity; - - return imageViewFragment; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - if (post == null) { - // No restoring - return null; - } else { - context = inflater.getContext(); - - imageView = new MultiImageView(context); - imageView.setCallback(this); - int padding = getResources().getDimensionPixelSize(R.dimen.image_view_padding); - imageView.setPadding(padding, padding, padding, padding); - - return imageView; - } - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // No restoring - if (post != null) { - if (!post.hasImage) { - throw new IllegalArgumentException("Post has no image"); - } - - // After layout has been done so getWidth & getHeight don't return 0 - imageView.post(new Runnable() { - @Override - public void run() { - // When the viewpager is created, it starts loading the first two views, - // then we set a position to the viewpager and it loads three more views! - // Check for these unused views here to avoid unnecessary loads - if (imageView.getWidth() == 0 || imageView.getHeight() == 0) - return; - - imageView.setThumbnail(post.thumbnailUrl); - - if (ChanSettings.getImageAutoLoad() && !post.spoiler) { - load(); - } else { - tapToLoad = true; - showProgressBar(false); - - if (post.ext.equals("webm")) { - isVideo = true; - activity.invalidateActionBar(); - } - } - } - }); - } - } - - private void load() { - if (loaded) return; - loaded = true; - - switch (post.ext) { - case "gif": - imageView.setGif(post.imageUrl); - break; - case "webm": - isVideo = true; - activity.invalidateActionBar(); - showProgressBar(false); - - if (tapToLoad) { - if (!videoVisible) { - startVideo(); - } else { - if (imageView.getVideoView() != null) { - imageView.getVideoView().start(); - } - } - } - break; - default: - imageView.setBigImage(post.imageUrl); - break; - } - - } - - @Override - public void onSaveInstanceState(Bundle bundle) { - // https://code.google.com/p/android/issues/detail?id=19917 - bundle.putString("bug_19917", "bug_19917"); - super.onSaveInstanceState(bundle); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (imageView != null) { - imageView.cancelLoad(); - } - } - - public void onSelected(ImageViewAdapter adapter, int position) { - activity.setProgressBarIndeterminateVisibility(showProgressBar); - - String filename = post.filename + "." + post.ext; - activity.getSupportActionBar().setTitle(filename); - - String text = (position + 1) + "/" + adapter.getCount(); - activity.getSupportActionBar().setSubtitle(text); - - activity.invalidateActionBar(); - - if (isVideo && ChanSettings.getVideoAutoPlay() && imageView != null) { - if (!videoVisible) { - startVideo(); - } else { - if (imageView.getVideoView() != null) { - imageView.getVideoView().start(); - } - } - } - - activity.setProgressBar(progressCurrent, progressTotal, progressDone); - } - - public void onDeselected() { - if (imageView != null && imageView.getVideoView() != null) { - imageView.getVideoView().pause(); - } - } - - public void onPrepareOptionsMenu(Menu menu) { - MenuItem item = menu.findItem(R.id.action_image_play_state); - item.setVisible(isVideo); - item.setEnabled(isVideo); - - if (imageView != null) { - VideoView view = imageView.getVideoView(); - if (view != null) { - item.setIcon((videoSetIconToPause || view.isPlaying()) ? R.drawable.ic_action_pause - : R.drawable.ic_action_play); - videoSetIconToPause = false; - } - } - } - - public void customOnOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_image_play_state: - if (!videoVisible) { - startVideo(); - } else { - VideoView view = imageView.getVideoView(); - if (view != null) { - if (!view.isPlaying()) { - view.start(); - } else { - view.pause(); - } - } - } - - activity.invalidateActionBar(); - break; - case R.id.action_open_browser: - AndroidUtils.openLink(post.imageUrl); - break; - case R.id.action_image_save: - case R.id.action_share: - if (ChanSettings.getImageShareUrl()) { - shareImageUrl(post.imageUrl); - } else { - ImageSaver.getInstance().saveImage(context, post.imageUrl, - ChanSettings.getImageSaveOriginalFilename() ? Long.toString(post.tim) : post.filename, post.ext, - item.getItemId() == R.id.action_share); - } - break; - default: - // Search if it was an ImageSearch item - for (ImageSearch engine : ImageSearch.engines) { - if (item.getItemId() == engine.getId()) { - AndroidUtils.openLink(engine.getUrl(post.imageUrl)); - break; - } - } - - break; - } - } - - private void shareImageUrl(String url) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, url); - context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share))); - } - - public void onVideoError(MultiImageView view) { - if (ChanSettings.getVideoErrorIgnore()) { - Toast.makeText(context, R.string.image_open_failed, Toast.LENGTH_SHORT).show(); - } else { - showVideoWarning(); - } - } - - @Override - public void onModeLoaded(MultiImageView multiImageView, MultiImageView.Mode mode) { - - } - - private void showVideoWarning() { - LinearLayout notice = new LinearLayout(context); - notice.setOrientation(LinearLayout.VERTICAL); - - TextView noticeText = new TextView(context); - noticeText.setText(R.string.video_playback_warning); - noticeText.setTextSize(16f); - notice.addView(noticeText, AndroidUtils.MATCH_WRAP_PARAMS); - - final CheckBox dontShowAgain = new CheckBox(context); - dontShowAgain.setText(R.string.video_playback_ignore); - notice.addView(dontShowAgain, AndroidUtils.MATCH_WRAP_PARAMS); - - int padding = dp(16f); - notice.setPadding(padding, padding, padding, padding); - - new AlertDialog.Builder(context) - .setTitle(R.string.video_playback_warning_title) - .setView(notice) - .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (dontShowAgain.isChecked()) { - ChanSettings.setVideoErrorIgnore(true); - } - } - }) - .setCancelable(false) - .show(); - } - - private void startVideo() { - if (videoVisible) return; - videoVisible = true; - - imageView.setVideo(post.imageUrl); - } - - public void showProgressBar(boolean e) { - showProgressBar = e; - activity.updateActionBarIfSelected(this); - } - - @Override - public void onTap(MultiImageView view) { - if (tapToLoad) { - if (loaded) { - activity.finish(); - } else { - load(); - } - } else { - activity.finish(); - } - } - - @Override - public void showProgress(MultiImageView view, boolean progress) { - showProgressBar(progress); - } - - @Override - public void onProgress(MultiImageView view, long current, long total) { - progressCurrent = current; - progressTotal = total; - progressDone = true; - activity.updateActionBarIfSelected(this); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java deleted file mode 100644 index 3ca1aafe..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/PostRepliesFragment.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.fragment; - -import android.app.Activity; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.presenter.ThreadPresenter; -import org.floens.chan.ui.helper.PostPopupHelper; -import org.floens.chan.ui.view.PostView; -import org.floens.chan.utils.ThemeHelper; - -/** - * A DialogFragment that shows a list of posts. Use the newInstance method for - * instantiating. - */ -public class PostRepliesFragment extends DialogFragment { - private ListView listView; - - private Activity activity; - private PostPopupHelper.RepliesData repliesData; - private PostPopupHelper postPopupHelper; - private ThreadPresenter presenter; - - public static PostRepliesFragment newInstance(PostPopupHelper.RepliesData repliesData, PostPopupHelper postPopupHelper, ThreadPresenter presenter) { - PostRepliesFragment fragment = new PostRepliesFragment(); - - fragment.repliesData = repliesData; - fragment.postPopupHelper = postPopupHelper; - fragment.presenter = presenter; - return fragment; - } - - public void dismissNoCallback() { - postPopupHelper = null; - dismiss(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setStyle(STYLE_NO_TITLE, 0); - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - - if (postPopupHelper != null) { -// postPopupHelper.onPostRepliesPop(); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup unused, Bundle savedInstanceState) { - View container; - if (ChanSettings.getReplyButtonsBottom()) { - container = inflater.inflate(R.layout.post_replies_bottombuttons, null); - } else { - container = inflater.inflate(R.layout.post_replies, null); - } - - listView = (ListView) container.findViewById(R.id.post_list); - - container.findViewById(R.id.replies_back).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - - container.findViewById(R.id.replies_close).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (postPopupHelper != null) { -// postPopupHelper.closeAllPostFragments(); - } - } - }); - - if (!ThemeHelper.getInstance().getTheme().isLightTheme) { - ((TextView) container.findViewById(R.id.replies_back_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_back_dark, 0, 0, 0); - ((TextView) container.findViewById(R.id.replies_close_icon)).setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_action_done_dark, 0, 0, 0); - } - - return container; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - activity = getActivity(); - - if (repliesData == null) { - // Restoring from background. - dismiss(); - } else { - ArrayAdapter adapter = new ArrayAdapter(activity, 0) { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - PostView postView; - if (convertView instanceof PostView) { - postView = (PostView) convertView; - } else { - postView = new PostView(activity); - } - - final Post p = getItem(position); - -// postView.setPost(p, presenter, false); -// postView.setHighlightQuotesWithNo(repliesData.forPost.no); - postView.setOnClickListeners(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (postPopupHelper != null) { - postPopupHelper.postClicked(p); - } - dismiss(); - } - }); - - return postView; - } - }; - - adapter.addAll(repliesData.posts); - listView.setAdapter(adapter); - - listView.setSelectionFromTop(repliesData.listViewIndex, repliesData.listViewTop); - listView.setOnScrollListener(new AbsListView.OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (repliesData != null) { - repliesData.listViewIndex = view.getFirstVisiblePosition(); - View v = view.getChildAt(0); - repliesData.listViewTop = (v == null) ? 0 : v.getTop(); - } - } - }); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ReplyFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ReplyFragment.java deleted file mode 100644 index 2f1c0368..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ReplyFragment.java +++ /dev/null @@ -1,609 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.fragment; - -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ViewFlipper; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.chan.ChanUrls; -import org.floens.chan.core.reply.ReplyManager; -import org.floens.chan.core.model.Board; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Reply; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.animation.ViewFlipperAnimations; -import org.floens.chan.ui.layout.CaptchaLayout; -import org.floens.chan.ui.view.LoadView; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.ImageDecoder; -import org.floens.chan.utils.Logger; -import org.floens.chan.utils.ThemeHelper; - -import java.io.File; - -import static org.floens.chan.utils.AndroidUtils.dp; - -public class ReplyFragment extends DialogFragment implements CaptchaLayout.CaptchaCallback { - private static final String TAG = "ReplyFragment"; - - private int page = 0; - - private Loadable loadable; - private boolean quickMode = false; - - private final Reply draft = new Reply(); - private boolean shouldSaveDraft = true; - - private String captchaResponse; - - private int defaultTextColor; - private int maxCommentCount; - - // Views - private View container; - private ViewFlipper flipper; - private Button cancelButton; - private ImageButton fileButton; - private Button submitButton; - private EditText nameView; - private EditText emailView; - private EditText subjectView; - private EditText commentView; - private EditText fileNameView; - private CheckBox spoilerImageView; - private LoadView imageViewContainer; - private CaptchaLayout captchaLayout; - private LoadView responseContainer; - private Button insertSpoiler; - private Button insertCode; - private TextView commentCountView; - private TextView fileStatusView; - - private AppCompatActivity context; - - public static ReplyFragment newInstance(Loadable loadable, boolean quickMode) { - ReplyFragment reply = new ReplyFragment(); - reply.loadable = loadable; - reply.quickMode = quickMode; - return reply; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - loadable.writeToBundle(context, outState); - outState.putBoolean(context.getPackageName() + ".quickmode", quickMode); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - context = (AppCompatActivity) getActivity(); - - if (loadable == null && savedInstanceState != null) { - loadable = new Loadable(); - loadable.readFromBundle(context, savedInstanceState); - quickMode = savedInstanceState.getBoolean(context.getPackageName() + ".quickmode"); - } - - if (loadable != null) { - setClosable(true); - - Dialog dialog = getDialog(); - String title = (loadable.isThreadMode() ? context.getString(R.string.reply_to_board) : context.getString(R.string.reply_to_board)) + " " + loadable.title; - - if (dialog == null) { - context.getSupportActionBar().setTitle(title); - } else { - dialog.setTitle(title); - // todo move elsewhere - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - dialog.setOnKeyListener(new Dialog.OnKeyListener() { - @Override - public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - onBackPressed(); - return true; - } else - return false; - } - }); - } - - Reply draft = null; - - if (TextUtils.isEmpty(draft.name)) { - draft.name = ChanSettings.getDefaultName(); - } - - nameView.setText(draft.name); - emailView.setText(draft.options); - subjectView.setText(draft.subject); - commentView.setText(draft.comment); - commentView.setSelection(draft.selection); - - setFile(draft.fileName, draft.file); - spoilerImageView.setChecked(draft.spoilerImage); - - if (loadable.isThreadMode()) { - subjectView.setVisibility(View.GONE); - } - - if (quickMode) { - nameView.setVisibility(View.GONE); - emailView.setVisibility(View.GONE); - subjectView.setVisibility(View.GONE); - } - - defaultTextColor = commentView.getCurrentTextColor(); - - Board b = ChanApplication.getBoardManager().getBoardByValue(loadable.board); - if (b != null) { - insertSpoiler.setVisibility(b.spoilers ? View.VISIBLE : View.GONE); - insertCode.setVisibility(b.codeTags ? View.VISIBLE : View.GONE); - maxCommentCount = b.maxCommentChars; - } - - commentView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - showCommentCount(); - } - }); - showCommentCount(); - - String baseUrl = loadable.isThreadMode() ? ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no) : ChanUrls.getBoardUrlDesktop(loadable.board); - captchaLayout.initCaptcha(baseUrl, ChanUrls.getCaptchaSiteKey(), - ThemeHelper.getInstance().getTheme().isLightTheme, ChanApplication.getInstance().getUserAgent(), this); - } else { - Logger.e(TAG, "Loadable in ReplyFragment was null"); - closeReply(); - } - } - - @Override - public void onPause() { - super.onPause(); - - ReplyManager replyManager = ChanApplication.getReplyManager(); - - if (shouldSaveDraft) { - draft.name = nameView.getText().toString(); - draft.options = emailView.getText().toString(); - draft.subject = subjectView.getText().toString(); - draft.comment = commentView.getText().toString(); - draft.fileName = fileNameView.getText().toString(); - draft.spoilerImage = spoilerImageView.isChecked(); - draft.selection = commentView.getSelectionStart(); - -// replyManager.setReplyDraft(draft); - } else { -// replyManager.removeReplyDraft(); - setFile(null, null); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - - ReplyManager replyManager = ChanApplication.getReplyManager(); - replyManager.removeFileListener(); - - context = null; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - // Setup the views with listeners - container = inflater.inflate(R.layout.reply_view, null); - flipper = (ViewFlipper) container.findViewById(R.id.reply_flipper); - - nameView = (EditText) container.findViewById(R.id.reply_name); - emailView = (EditText) container.findViewById(R.id.reply_email); - subjectView = (EditText) container.findViewById(R.id.reply_subject); - commentView = (EditText) container.findViewById(R.id.reply_comment); - commentView.requestFocus(); - fileNameView = (EditText) container.findViewById(R.id.reply_file_name); - spoilerImageView = (CheckBox) container.findViewById(R.id.reply_spoiler_image); - - imageViewContainer = (LoadView) container.findViewById(R.id.reply_image); - responseContainer = (LoadView) container.findViewById(R.id.reply_response); - captchaLayout = (CaptchaLayout) container.findViewById(R.id.captcha_layout); - - cancelButton = (Button) container.findViewById(R.id.reply_cancel); - cancelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (page == 1) { - flipPage(0); - } else { - closeReply(); - } - } - }); - - fileButton = (ImageButton) container.findViewById(R.id.reply_file); - fileButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (draft.file == null) { - /*ChanApplication.getReplyManager().pickFile(new ReplyManager.FileListener() { - @Override - public void onFile(String name, File file) { - setFile(name, file); - } - - @Override - public void onFileLoading() { - imageViewContainer.setVisibility(View.VISIBLE); - imageViewContainer.setView(null); - } - });*/ - } else { - setFile(null, null); - } - } - }); - - submitButton = (Button) container.findViewById(R.id.reply_submit); - submitButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (page == 1 || ChanSettings.passLoggedIn()) { - flipPage(2); - submit(); - } else { - flipPage(1); - } - } - }); - - insertSpoiler = (Button) container.findViewById(R.id.insert_spoiler); - insertSpoiler.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - insertAtCursor("[spoiler]", "[/spoiler]"); - } - }); - - insertCode = (Button) container.findViewById(R.id.insert_code); - insertCode.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - insertAtCursor("[code]", "[/code]"); - } - }); - - commentCountView = (TextView) container.findViewById(R.id.reply_comment_counter); - - fileStatusView = (TextView) container.findViewById(R.id.reply_file_status); - - return container; - } - - public boolean onBackPressed() { - if (page == 1) { - flipPage(0); - return false; - } else { - return true; - } - } - - @Override - public void captchaLoaded(CaptchaLayout captchaLayout) { - } - - @Override - public void captchaEntered(CaptchaLayout captchaLayout, String response) { - captchaResponse = response; - if (page == 1) { - flipPage(2); - submit(); - } - } - - private void insertAtCursor(String before, String after) { - int pos = commentView.getSelectionStart(); - String text = commentView.getText().toString(); - text = new StringBuilder(text).insert(pos, before + after).toString(); - commentView.setText(text); - commentView.setSelection(pos + before.length()); - } - - private void showCommentCount() { - int count = commentView.getText().length(); - commentCountView.setText(count + "/" + maxCommentCount); - if (count > maxCommentCount) { - commentCountView.setTextColor(0xffff0000); - } else { - commentCountView.setTextColor(defaultTextColor); - } - } - - private void closeReply() { - if (getDialog() != null) { - dismissAllowingStateLoss(); - } else { - context.finish(); - } - } - - /** - * Set if the dialog is able to be closed, by pressing outside of the - * dialog, or something else. - */ - private void setClosable(boolean e) { - if (getDialog() != null) { - getDialog().setCanceledOnTouchOutside(e); - setCancelable(e); - } - } - - /** - * Flip to an page with an animation. Sets the correct text on the - * cancelButton: - * - * @param position 0-2 - */ - private void flipPage(int position) { - boolean flipBack = position < page; - - page = position; - - if (flipBack) { - flipper.setInAnimation(ViewFlipperAnimations.BACK_IN); - flipper.setOutAnimation(ViewFlipperAnimations.BACK_OUT); - } else { - flipper.setInAnimation(ViewFlipperAnimations.NEXT_IN); - flipper.setOutAnimation(ViewFlipperAnimations.NEXT_OUT); - } - flipper.setDisplayedChild(position); - - if (page == 0) { - cancelButton.setText(R.string.cancel); - } else if (page == 1) { - cancelButton.setText(R.string.back); - } else if (page == 2) { - cancelButton.setText(R.string.close); - } - - if (page == 1) { - captchaLayout.load(); - submitButton.setEnabled(captchaResponse != null); - } else if (page == 0) { - submitButton.setEnabled(true); - } - } - - /** - * Set the picked image in the imageView. Sets the file in the draft. Call - * null on the file to empty the imageView. - * - * @param name the filename - * @param file the file - */ - private void setFile(final String name, final File file) { - draft.file = file; - draft.fileName = name; - - if (file == null) { - fileButton.setImageResource(ThemeHelper.getInstance().getTheme().isLightTheme ? R.drawable.ic_action_attachment : R.drawable.ic_action_attachment_dark); - imageViewContainer.removeAllViews(); - imageViewContainer.setVisibility(View.GONE); - fileNameView.setText(""); - fileNameView.setVisibility(View.GONE); - spoilerImageView.setVisibility(View.GONE); - spoilerImageView.setChecked(false); - fileStatusView.setVisibility(View.GONE); - } else { - fileButton.setImageResource(ThemeHelper.getInstance().getTheme().isLightTheme ? R.drawable.ic_action_cancel : R.drawable.ic_action_cancel_dark); - fileNameView.setVisibility(View.VISIBLE); - fileNameView.setText(name); - - Board b = ChanApplication.getBoardManager().getBoardByValue(loadable.board); - spoilerImageView.setVisibility(b != null && b.spoilers ? View.VISIBLE : View.GONE); - - if (b != null) { - boolean probablyWebm = name.endsWith(".webm"); - int maxSize = probablyWebm ? b.maxWebmSize : b.maxFileSize; - if (file.length() > maxSize) { - String fileSize = AndroidUtils.getReadableFileSize((int) file.length(), false); - String maxSizeString = AndroidUtils.getReadableFileSize(maxSize, false); - String text = getString(probablyWebm ? R.string.reply_webm_too_big : R.string.reply_file_too_big, fileSize, maxSizeString); - fileStatusView.setVisibility(View.VISIBLE); - fileStatusView.setText(text); - } else { - fileStatusView.setVisibility(View.GONE); - } - } - - imageViewContainer.setVisibility(View.VISIBLE); - imageViewContainer.setView(null); - imageViewContainer.post(new Runnable() { - public void run() { - if (file.length() < 10 * 1024 * 1024) { - new Thread(new Runnable() { - @Override - public void run() { - if (context == null) - return; - - final Bitmap bitmap = ImageDecoder.decodeFile(file, imageViewContainer.getWidth(), imageViewContainer.getWidth()); - - context.runOnUiThread(new Runnable() { - @Override - public void run() { - if (context != null) { - if (bitmap != null) { - ImageView imageView = new ImageView(context); - imageViewContainer.setView(imageView); - imageView.setAdjustViewBounds(true); - imageView.setMaxWidth(imageViewContainer.getWidth()); - imageView.setMaxHeight(imageViewContainer.getWidth()); - imageView.setImageBitmap(bitmap); - } else { - noPreview(imageViewContainer); - } - } - } - }); - } - }).start(); - } else { - noPreview(imageViewContainer); - } - } - }); - } - } - - private void noPreview(LoadView loadView) { - TextView text = new TextView(context); - text.setLayoutParams(AndroidUtils.MATCH_WRAP_PARAMS); - text.setGravity(Gravity.CENTER); - text.setText(R.string.reply_no_preview); - text.setTextSize(16f); - int padding = dp(16); - text.setPadding(padding, padding, padding, padding); - loadView.setView(text); - } - - /** - * Submit button clicked at page 1 - */ - private void submit() { - submitButton.setEnabled(false); - cancelButton.setEnabled(false); - setClosable(false); - - responseContainer.setView(null); - - draft.name = nameView.getText().toString(); - draft.options = emailView.getText().toString(); - draft.subject = subjectView.getText().toString(); - draft.comment = commentView.getText().toString(); - draft.captchaResponse = captchaResponse; - - draft.fileName = "image"; - String n = fileNameView.getText().toString(); - if (!TextUtils.isEmpty(n)) { - draft.fileName = n; - } - - draft.resto = loadable.isThreadMode() ? loadable.no : -1; - draft.board = loadable.board; - - if (ChanSettings.getPassEnabled()) { - draft.usePass = true; - draft.passId = ChanSettings.getPassId(); - } - - Board b = ChanApplication.getBoardManager().getBoardByValue(loadable.board); - draft.spoilerImage = b != null && b.spoilers && spoilerImageView.isChecked(); - - ChanApplication.getReplyManager().postReply(draft, new ReplyManager.ReplyListener() { - @Override - public void onResponse(ReplyManager.ReplyResponse response) { - handleSubmitResponse(response); - } - }); - } - - /** - * Got response about or reply from ReplyManager - * - * @param response - */ - private void handleSubmitResponse(ReplyManager.ReplyResponse response) { - if (context == null) - return; - - if (response.isNetworkError || response.isUserError) { - int resId = R.string.reply_error; - Toast.makeText(context, resId, Toast.LENGTH_LONG).show(); - submitButton.setEnabled(true); - cancelButton.setEnabled(true); - setClosable(true); - captchaResponse = null; - if (ChanSettings.passLoggedIn()) { - flipPage(0); - } else { - flipPage(1); - captchaLayout.reset(); - } - } else if (response.isSuccessful) { - shouldSaveDraft = false; - Toast.makeText(context, R.string.reply_success, Toast.LENGTH_SHORT).show(); - - // Pin thread on successful post - if (ChanSettings.getPinOnPost() && loadable.isThreadMode()) { - ChanApplication.getWatchManager().addPin(loadable); - } - - closeReply(); - } else { - cancelButton.setEnabled(true); - setClosable(true); - - WebView webView = new WebView(context); - WebSettings settings = webView.getSettings(); - settings.setSupportZoom(true); - - webView.loadData(response.responseData, "text/html", null); - - responseContainer.setView(webView); - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java deleted file mode 100644 index e80c6af3..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/SettingsFragment.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.fragment; - -import android.content.Intent; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceFragment; -import android.preference.PreferenceGroup; -import android.view.View; -import android.widget.ListView; -import android.widget.Toast; - -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.ui.activity.AboutActivity; -import org.floens.chan.ui.activity.BaseActivity; -import org.floens.chan.ui.activity.LicenseActivity; -import org.floens.chan.ui.activity.SettingsActivity; -import org.floens.chan.utils.ThemeHelper; - -public class SettingsFragment extends PreferenceFragment { - private int clickCount = 0; - private boolean argumentsRead = false; - - private Preference developerPreference; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preference); - - Preference aboutLicences = findPreference("about_licenses"); - if (aboutLicences != null) { - aboutLicences.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(getActivity(), AboutActivity.class)); - - return true; - } - }); - } - - Preference aboutLicence = findPreference("about_license"); - if (aboutLicence != null) { - aboutLicence.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(getActivity(), LicenseActivity.class)); - - return true; - } - }); - } - - Preference aboutVersion = findPreference("about_version"); - if (aboutVersion != null) { - aboutVersion.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - if (++clickCount >= 5) { - clickCount = 0; - - boolean enabled = !ChanSettings.getDeveloper(); - ChanSettings.setDeveloper(enabled); - updateDeveloperPreference(); - - Toast.makeText(getActivity(), (enabled ? "Enabled " : "Disabled ") + "developer options", - Toast.LENGTH_LONG).show(); - } - - return true; - } - }); - - String version = ""; - try { - version = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName; - } catch (NameNotFoundException e) { - e.printStackTrace(); - } - - aboutVersion.setTitle(R.string.app_name); - aboutVersion.setSummary(version); - } - - developerPreference = findPreference("about_developer"); - ((PreferenceGroup) findPreference("group_about")).removePreference(developerPreference); - updateDeveloperPreference(); - - final ListPreference theme = (ListPreference) findPreference("preference_theme"); - String currentValue = theme.getValue(); - if (currentValue == null) { - theme.setValue((String) theme.getEntryValues()[0]); - currentValue = theme.getValue(); - } - updateSummary(theme, currentValue); - - theme.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateSummary(theme, newValue.toString()); - - // Thanks! https://github.com/CyanogenMod/android_packages_apps_Calculator/blob/cm-10.2/src/com/android/calculator2/view/PreferencesFragment.java - if (!newValue.toString().equals(ThemeHelper.getInstance().getTheme().name)) { - Intent intent = new Intent(getActivity(), SettingsActivity.class); - - intent.putExtra("pos", getListView().getFirstVisiblePosition()); - View child = getListView().getChildAt(0); - intent.putExtra("off", child != null ? child.getTop() : 0); - - ((SettingsActivity) getActivity()).restart(intent); - } - - return true; - } - }); - - final ListPreference font = (ListPreference) findPreference("preference_font"); - String currentFontValue = font.getValue(); - if (currentFontValue == null) { - font.setValue((String) font.getEntryValues()[0]); - currentFontValue = font.getValue(); - } - updateSummary(font, currentFontValue); - - font.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - updateSummary(font, newValue.toString()); - BaseActivity.doRestartOnResume = true; - - return true; - } - }); - } - - public void onStart() { - super.onStart(); - - final Bundle args = getArguments(); - if (args != null && !argumentsRead) { - argumentsRead = true; - getListView().setSelectionFromTop(args.getInt("pos", 0), args.getInt("off", 0)); - } - } - - @Override - public void onResume() { - super.onResume(); - - final Preference watchPreference = findPreference("watch_settings"); - if (watchPreference != null) { - watchPreference.setSummary(ChanSettings.getWatchEnabled() ? R.string.watch_summary_enabled - : R.string.watch_summary_disabled); - } - - final Preference passPreference = findPreference("pass_settings"); - if (passPreference != null) { - passPreference.setSummary(ChanSettings.getPassEnabled() ? R.string.pass_summary_enabled - : R.string.pass_summary_disabled); - } - } - - private ListView getListView() { - return (ListView) getView().findViewById(android.R.id.list); - } - - private void updateDeveloperPreference() { - if (ChanSettings.getDeveloper()) { - ((PreferenceGroup) findPreference("group_about")).addPreference(developerPreference); - } else { - ((PreferenceGroup) findPreference("group_about")).removePreference(developerPreference); - } - } - - private void updateSummary(ListPreference list, String value) { - int index = list.findIndexOfValue(value); - list.setSummary(list.getEntries()[index]); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java b/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java deleted file mode 100644 index 810eab92..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/fragment/ThreadFragment.java +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.fragment; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.Button; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.RelativeLayout; -import android.widget.SearchView; -import android.widget.TextView; - -import com.android.volley.NetworkError; -import com.android.volley.NoConnectionError; -import com.android.volley.ServerError; -import com.android.volley.VolleyError; - -import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; -import org.floens.chan.core.loader.ChanLoader; -import org.floens.chan.core.loader.EndOfLineException; -import org.floens.chan.core.manager.ThreadManager; -import org.floens.chan.core.model.ChanThread; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Post; -import org.floens.chan.ui.activity.BaseActivity; -import org.floens.chan.ui.activity.ImageViewActivity; -import org.floens.chan.ui.adapter.PostAdapter; -import org.floens.chan.ui.view.LoadView; -import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.ImageSaver; -import org.floens.chan.utils.ThemeHelper; - -import java.util.ArrayList; -import java.util.List; - -import javax.net.ssl.SSLException; - -import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.setItemBackground; - -public class ThreadFragment extends Fragment implements ThreadManager.ThreadManagerListener, PostAdapter.PostAdapterCallback { - private ThreadManager threadManager; - private Loadable loadable; - - private PostAdapter postAdapter; - private LoadView container; - private AbsListView listView; - private ImageView skip; - private FilterView filterView; - - private SkipLogic skipLogic; - private int highlightedPost = -1; - private ThreadManager.ViewMode viewMode = ThreadManager.ViewMode.LIST; - private boolean isFiltering = false; - - public static ThreadFragment newInstance(BaseActivity activity) { - ThreadFragment fragment = new ThreadFragment(); - fragment.threadManager = new ThreadManager(activity, fragment); - - return fragment; - } - - public void bindLoadable(Loadable l) { - if (loadable != null) { - threadManager.unbindLoader(); - } - - setEmpty(); - - loadable = l; - threadManager.bindLoader(loadable); - } - - public void requestData() { - threadManager.requestData(); - } - - public void requestNextData() { - threadManager.requestNextData(); - } - - public void reload() { - setEmpty(); - - threadManager.requestData(); - } - - public void openReply() { - if (threadManager.hasLoader()) { - threadManager.openReply(true); - } - } - - public boolean hasLoader() { - return threadManager.hasLoader(); - } - - public void setViewMode(ThreadManager.ViewMode viewMode) { - this.viewMode = viewMode; - } - - public ChanLoader getLoader() { - return threadManager.getChanLoader(); - } - - public void startFiltering() { - if (filterView != null) { - isFiltering = true; - filterView.setVisibility(View.VISIBLE); - filterView.focusSearch(); - } - } - - public void highlightPost(int no) { - highlightedPost = no; - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (threadManager != null) { - threadManager.onDestroy(); - } - threadManager = null; - loadable = null; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setHasOptionsMenu(true); - } - - @Override - public void onStart() { - super.onStart(); - - if (threadManager != null) { - threadManager.onStart(); - } - } - - @Override - public void onStop() { - super.onStop(); - - if (threadManager != null) { - threadManager.onStop(); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - container = new LoadView(inflater.getContext()); - if (loadable == null) { - container.setView(getCenteredMessageView(R.string.thread_not_specified)); - } - - return container; - } - - @Override - public void onPostClicked(Post post) { - if (loadable.isBoardMode() || loadable.isCatalogMode()) { - ((BaseActivity) getActivity()).onOPClicked(post); - } else if (loadable.isThreadMode() && isFiltering) { - filterView.clearSearch(); -// postAdapter.scrollToPost(post.no); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (loadable.isThreadMode()) { - switch (item.getItemId()) { - case R.id.action_download_album: - // Get the posts with images - ArrayList imagePosts = new ArrayList<>(); -// for (Post post : postAdapter.getList()) { -// if (post.hasImage) { -// imagePosts.add(post); -// } -// } - if (imagePosts.size() > 0) { - List list = new ArrayList<>(); - - String folderName = Post.generateTitle(imagePosts.get(0), 10); - - String filename; - for (Post post : imagePosts) { - filename = (ChanSettings.getImageSaveOriginalFilename() ? post.tim : post.filename) + "." + post.ext; - list.add(new ImageSaver.DownloadPair(post.imageUrl, filename)); - } - - ImageSaver.getInstance().saveAll(getActivity(), folderName, list); - } - - return true; - } - } - - return super.onOptionsItemSelected(item); - } - - @Override - public void onThumbnailClicked(Post source) { - if (postAdapter != null) { - ImageViewActivity.launch(getActivity(), postAdapter, source.no, threadManager); - } - } - - @Override - public void onScrollTo(int post) { - if (postAdapter != null) { -// postAdapter.scrollToPost(post); - } - } - - @Override - public void onRefreshView() { - if (postAdapter != null) { - postAdapter.notifyDataSetChanged(); - } - } - - @Override - public void onOpenThread(final Loadable thread, int highlightedPost) { - ((BaseActivity) getActivity()).onOpenThread(thread); - this.highlightedPost = highlightedPost; - } - - @Override - public ThreadManager.ViewMode getViewMode() { - return viewMode; - } - - @Override - public void onThreadLoaded(ChanThread thread) { - if (postAdapter == null) { - if (container != null) { - container.setView(createView()); - } - } - -// postAdapter.setError(null); - postAdapter.setThread(thread); - - if (highlightedPost >= 0) { - threadManager.highlightPost(highlightedPost); -// postAdapter.scrollToPost(highlightedPost); - highlightedPost = -1; - } - - ((BaseActivity) getActivity()).onThreadLoaded(thread); - } - - @Override - public void onThreadLoadError(VolleyError error) { - if (error instanceof EndOfLineException) { -// postAdapter.setEndOfLine(true); - } else { - if (postAdapter == null) { - if (container != null) { - container.setView(getLoadErrorView(error)); - } - } else { -// postAdapter.setError(getLoadErrorText(error)); - } - } - - highlightedPost = -1; - } - - public void onFilteredResults(String filter, int count, boolean all) { - isFiltering = !all; - - if (filterView != null) { - filterView.setText(filter, count, all); - } - } - - @Override - public Loadable getLoadable() { - return loadable; - } - - @Override - public void onListScrolledToBottom() { - - } - - private RelativeLayout createView() { - RelativeLayout compound = new RelativeLayout(getActivity()); - - LinearLayout listViewContainer = new LinearLayout(getActivity()); - listViewContainer.setOrientation(LinearLayout.VERTICAL); - - filterView = new FilterView(getActivity()); - filterView.setVisibility(View.GONE); - listViewContainer.addView(filterView, AndroidUtils.MATCH_WRAP_PARAMS); - - if (viewMode == ThreadManager.ViewMode.LIST) { - ListView list = new ListView(getActivity()); - listView = list; -// postAdapter = new PostAdapter(getActivity(), threadManager, listView, this); -// listView.setAdapter(postAdapter); - list.setSelectionFromTop(loadable.listViewIndex, loadable.listViewTop); - } else if (viewMode == ThreadManager.ViewMode.GRID) { - GridView grid = new GridView(getActivity()); - grid.setNumColumns(GridView.AUTO_FIT); - int postGridWidth = getActivity().getResources().getDimensionPixelSize(R.dimen.post_grid_width); - grid.setColumnWidth(postGridWidth); - listView = grid; -// postAdapter = new PostAdapter(getActivity(), threadManager, listView, this); -// listView.setAdapter(postAdapter); - listView.setSelection(loadable.listViewIndex); - } - - listView.setOnScrollListener(new OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - if (!isFiltering) { - if (skipLogic != null) { - skipLogic.onScrollStateChanged(view, scrollState); - } - } - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (!isFiltering) { - if (loadable != null) { - int index = view.getFirstVisiblePosition(); - View v = view.getChildAt(0); - int top = v == null ? 0 : v.getTop(); - if (index != 0 || top != 0) { - loadable.listViewIndex = index; - loadable.listViewTop = top; - } - } - if (skipLogic != null) { - skipLogic.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); - } - } - } - }); - - listViewContainer.addView(listView, AndroidUtils.MATCH_PARAMS); - - compound.addView(listViewContainer, AndroidUtils.MATCH_PARAMS); - - if (loadable.isThreadMode()) { - skip = new ImageView(getActivity()); - skip.setImageResource(R.drawable.skip_arrow_down); - skip.setVisibility(View.GONE); - compound.addView(skip, AndroidUtils.WRAP_PARAMS); - - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) skip.getLayoutParams(); - params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - params.setMargins(0, 0, dp(8), dp(8)); - skip.setLayoutParams(params); - - skipLogic = new SkipLogic(skip, listView); - } - - return compound; - } - - private void setEmpty() { - postAdapter = null; - - if (container != null) { - container.setView(null); - } - - if (listView != null) { - listView.setOnScrollListener(null); - listView = null; - } - - skip = null; - skipLogic = null; - filterView = null; - } - - private void doFilter(String filter) { - if (postAdapter != null) { -// postAdapter.setFilter(filter); - } - } - - /** - * Returns an TextView containing the appropriate error message - * - * @param error - * @return - */ - private View getLoadErrorView(VolleyError error) { - String errorMessage = getLoadErrorText(error); - - LinearLayout wrapper = new LinearLayout(getActivity()); - wrapper.setLayoutParams(AndroidUtils.MATCH_PARAMS); - wrapper.setGravity(Gravity.CENTER); - wrapper.setOrientation(LinearLayout.VERTICAL); - - TextView text = new TextView(getActivity()); - text.setLayoutParams(AndroidUtils.WRAP_PARAMS); - text.setText(errorMessage); - text.setTextSize(24f); - wrapper.addView(text); - - Button retry = new Button(getActivity()); - retry.setText(R.string.thread_load_failed_retry); - retry.setLayoutParams(AndroidUtils.WRAP_PARAMS); - retry.setGravity(Gravity.CENTER); - retry.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (threadManager != null) { - reload(); - } - } - }); - - wrapper.addView(retry); - - LinearLayout.LayoutParams retryParams = (LinearLayout.LayoutParams) retry.getLayoutParams(); - retryParams.topMargin = dp(12); - retry.setLayoutParams(retryParams); - - return wrapper; - } - - private String getLoadErrorText(VolleyError error) { - String errorMessage; - - if (error.getCause() instanceof SSLException) { - errorMessage = getString(R.string.thread_load_failed_ssl); - } else if ((error instanceof NoConnectionError) || (error instanceof NetworkError)) { - errorMessage = getString(R.string.thread_load_failed_network); - } else if (error instanceof ServerError) { - errorMessage = getString(R.string.thread_load_failed_server); - } else { - errorMessage = getString(R.string.thread_load_failed_parsing); - } - - return errorMessage; - } - - private View getCenteredMessageView(int stringResourceId) { - LinearLayout layout = new LinearLayout(getActivity()); - layout.setGravity(Gravity.CENTER); - TextView messageView = new TextView(getActivity()); - messageView.setText(getString(stringResourceId)); - layout.addView(messageView); - - return layout; - } - - private static class SkipLogic { - private final ImageView skip; - private int lastFirstVisibleItem; - private int lastTop; - private boolean up = false; - private final AbsListView listView; - - public SkipLogic(ImageView skipView, AbsListView list) { - skip = skipView; - listView = list; - skip.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (up) { - listView.setSelection(0); - } else { - listView.setSelection(listView.getCount() - 1); - } - skip.setVisibility(View.GONE); - } - }); - } - - public void onScrollStateChanged(AbsListView view, int scrollState) { - if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { - skip.setVisibility(View.VISIBLE); - } - } - - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - View v = view.getChildAt(0); - int top = (v == null) ? 0 : v.getTop(); - - if (firstVisibleItem == lastFirstVisibleItem) { - if (top > lastTop) { - onUp(); - } else if (top < lastTop) { - onDown(); - } - } else { - if (firstVisibleItem > lastFirstVisibleItem) { - onDown(); - } else { - onUp(); - } - } - lastFirstVisibleItem = firstVisibleItem; - lastTop = top; - } - - private void onUp() { - skip.setImageResource(R.drawable.skip_arrow_up); - up = true; - } - - private void onDown() { - skip.setImageResource(R.drawable.skip_arrow_down); - up = false; - } - } - - public class FilterView extends LinearLayout { - private SearchView searchView; - private TextView textView; - - public FilterView(Context activity) { - super(activity); - init(); - } - - public FilterView(Context activity, AttributeSet attr) { - super(activity, attr); - init(); - } - - public FilterView(Context activity, AttributeSet attr, int style) { - super(activity, attr, style); - init(); - } - - public void focusSearch() { - searchView.requestFocus(); - } - - public void clearSearch() { - searchView.setQuery("", false); - doFilter(""); - setVisibility(View.GONE); - } - - private void init() { - setOrientation(LinearLayout.VERTICAL); - - LinearLayout searchViewContainer = new LinearLayout(getContext()); - searchViewContainer.setOrientation(LinearLayout.HORIZONTAL); - - searchView = new SearchView(getContext()); - searchView.setIconifiedByDefault(false); - searchView.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); - searchViewContainer.addView(searchView); - LinearLayout.LayoutParams searchViewParams = (LinearLayout.LayoutParams) searchView.getLayoutParams(); - searchViewParams.weight = 1f; - searchViewParams.width = 0; - searchViewParams.height = LayoutParams.MATCH_PARENT; - searchView.setLayoutParams(searchViewParams); - - ImageView closeButton = new ImageView(getContext()); - searchViewContainer.addView(closeButton); - closeButton.setImageResource(ThemeHelper.getInstance().getTheme().isLightTheme ? R.drawable.ic_action_cancel : R.drawable.ic_action_cancel_dark); - LinearLayout.LayoutParams closeButtonParams = (LinearLayout.LayoutParams) closeButton.getLayoutParams(); - searchViewParams.width = dp(48); - searchViewParams.height = LayoutParams.MATCH_PARENT; - closeButton.setLayoutParams(closeButtonParams); - setItemBackground(closeButton); - int padding = dp(8); - closeButton.setPadding(padding, padding, padding, padding); - - closeButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - clearSearch(); - } - }); - - addView(searchViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, dp(48))); - - searchView.setQueryHint(getString(R.string.search_hint)); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - doFilter(query); - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - doFilter(newText); - return false; - } - }); - - textView = new TextView(getContext()); - textView.setGravity(Gravity.CENTER); - addView(textView, new LayoutParams(LayoutParams.MATCH_PARENT, dp(28))); - } - - private void setText(String filter, int count, boolean all) { - if (all) { - textView.setText(""); - } else { -// String posts = getContext().getString(count == 1 ? R.string.one_post : R.string.multiple_posts); -// String text = getContext().getString(R.string.search_results, Integer.toString(count), posts, filter); -// textView.setText(text); - } - } - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java index 26961bcd..1bff0825 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/PostHelper.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.helper; import android.content.res.Resources; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java index 64a94ee4..6f7f6e79 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeItemAnimator.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.helper; import android.support.v4.view.ViewCompat; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java index ef7c7f28..96f698b3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/helper/SwipeListener.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.helper; import android.content.Context; @@ -35,7 +52,6 @@ public class SwipeListener extends RecyclerView.ItemDecoration implements Recycl private final int flingPixels; private final int maxFlingPixels; - private final Context context; private Callback callback; private final RecyclerView recyclerView; private final LinearLayoutManager layoutManager; @@ -83,7 +99,6 @@ public class SwipeListener extends RecyclerView.ItemDecoration implements Recycl }; public SwipeListener(Context context, RecyclerView rv, Callback callback) { - this.context = context; recyclerView = rv; this.callback = callback; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java index d7b7e388..9fbe21a3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.layout; import android.content.Context; @@ -326,7 +343,7 @@ public class ReplyLayout extends LoadView implements View.OnClickListener, Anima @Override public void onFilePickError() { - Toast.makeText(getContext(), R.string.file_open_failed, Toast.LENGTH_LONG).show(); + Toast.makeText(getContext(), R.string.reply_file_open_failed, Toast.LENGTH_LONG).show(); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java index 3b6d6a49..4ebd5dde 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java @@ -199,12 +199,12 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres ClipboardManager clipboard = (ClipboardManager) AndroidUtils.getAppRes().getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Post text", post.comment.toString()); clipboard.setPrimaryClip(clip); - Toast.makeText(getContext(), R.string.post_text_copied_to_clipboard, Toast.LENGTH_SHORT).show(); + Toast.makeText(getContext(), R.string.post_text_copied, Toast.LENGTH_SHORT).show(); } @Override public void openLink(final String link) { - if (ChanSettings.getOpenLinkConfirmation()) { + if (ChanSettings.openLinkConfirmation.get()) { new AlertDialog.Builder(getContext()) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @@ -223,7 +223,7 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres @Override public void openWebView(String title, String link) { - AndroidUtils.openWebView((Activity)getContext(), title, link); + AndroidUtils.openWebView((Activity) getContext(), title, link); } @Override diff --git a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java index 836d0ca0..20c5a9e8 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java @@ -26,16 +26,14 @@ import android.content.Intent; import android.os.IBinder; import android.support.v4.app.NotificationCompat; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; -import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Post; +import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.watch.PinWatcher; import org.floens.chan.ui.activity.BoardActivity; -import org.floens.chan.ui.activity.ChanActivity; -import org.floens.chan.ui.activity.StartActivity; import org.floens.chan.utils.AndroidUtils; import java.util.ArrayList; @@ -61,7 +59,7 @@ public class WatchNotifier extends Service { super.onCreate(); nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - wm = ChanApplication.getWatchManager(); + wm = Chan.getWatchManager(); startForeground(NOTIFICATION_ID, createNotification()); } @@ -92,8 +90,8 @@ public class WatchNotifier extends Service { } private Notification createNotification() { - boolean notifyQuotesOnly = ChanSettings.getWatchNotifyMode().equals("quotes"); - boolean soundQuotesOnly = ChanSettings.getWatchSound().equals("quotes"); + boolean notifyQuotesOnly = ChanSettings.watchNotifyMode.get().equals("quotes"); + boolean soundQuotesOnly = ChanSettings.watchSound.get().equals("quotes"); List list = new ArrayList<>(); List listQuoting = new ArrayList<>(); @@ -138,7 +136,7 @@ public class WatchNotifier extends Service { } } - if (ChanApplication.getInstance().getApplicationInForeground()) { + if (Chan.getInstance().getApplicationInForeground()) { ticker = false; sound = false; } @@ -210,7 +208,6 @@ public class WatchNotifier extends Service { * @param expandedLines A list of lines for the big notification, or null if not shown * @param makeSound Should the notification make a sound * @param target The target pin, or null to open the pinned pane on tap - * @return */ @SuppressWarnings("deprecation") private Notification getNotificationFor(String tickerText, String contentTitle, String contentText, int contentNumber, @@ -231,7 +228,7 @@ public class WatchNotifier extends Service { } if (light) { - long watchLed = ChanSettings.getWatchLed(); + long watchLed = Long.parseLong(ChanSettings.watchLed.get(), 16); if (watchLed >= 0) { builder.setLights((int) watchLed, 1000, 1000); } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/BooleanSettingView.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/BooleanSettingView.java index bf9b180e..8612dd67 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/BooleanSettingView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/BooleanSettingView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.support.v7.widget.SwitchCompat; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/LinkSettingView.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/LinkSettingView.java index b0a4e1d6..5bdb0af8 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/LinkSettingView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/LinkSettingView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.view.View; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/ListSettingView.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/ListSettingView.java index 022b5cac..2a485ff0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/ListSettingView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/ListSettingView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.view.Gravity; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingView.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingView.java index 5bb66e7f..9d4593de 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.view.View; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java index d0ac8c34..3b525b2f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsGroup.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsGroup.java index 10352e81..3a69a21c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsGroup.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsGroup.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import java.util.ArrayList; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/StringSettingView.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/StringSettingView.java index 5edbb1b6..d94a740c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/StringSettingView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/StringSettingView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.settings; import android.app.AlertDialog; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java index 6255663e..1ecfc495 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java @@ -20,9 +20,7 @@ package org.floens.chan.ui.toolbar; import android.content.Context; import android.view.View; import android.widget.LinearLayout; -import android.widget.TextView; -import org.floens.chan.R; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java index 66780594..c606e906 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java @@ -22,9 +22,9 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; -import android.os.Build; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -54,7 +54,7 @@ import java.util.List; import static org.floens.chan.utils.AndroidUtils.dp; import static org.floens.chan.utils.AndroidUtils.getAttrColor; -import static org.floens.chan.utils.AndroidUtils.getAttrDrawable; +import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; public class Toolbar extends LinearLayout implements View.OnClickListener, LoadView.Listener { private ImageView arrowMenuView; @@ -150,11 +150,7 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV arrowMenuDrawable = new ArrowMenuDrawable(); arrowMenuView.setImageDrawable(arrowMenuDrawable); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - arrowMenuView.setBackground(getAttrDrawable(getContext(), android.R.attr.selectableItemBackgroundBorderless)); - } else { - arrowMenuView.setBackgroundResource(R.drawable.item_background); - } + setRoundItemBackground(arrowMenuView); leftButtonContainer.addView(arrowMenuView, new FrameLayout.LayoutParams(dp(56), FrameLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER_VERTICAL)); @@ -352,6 +348,7 @@ public class Toolbar extends LinearLayout implements View.OnClickListener, LoadV return searchViewWrapper; } else { + @SuppressLint("InflateParams") LinearLayout menu = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.toolbar_menu, null); menu.setGravity(Gravity.CENTER_VERTICAL); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java index 75778ba9..fb74a947 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenu.java @@ -64,7 +64,7 @@ public class ToolbarMenu extends LinearLayout { } public ToolbarMenuItem createOverflow(ToolbarMenuItem.ToolbarMenuItemCallback callback) { - ToolbarMenuItem overflow = addItem(new ToolbarMenuItem(getContext(), callback, 100, 100, R.drawable.ic_more)); + ToolbarMenuItem overflow = addItem(new ToolbarMenuItem(getContext(), callback, 100, 100, R.drawable.ic_more_vert_white_24dp)); ImageView overflowImage = overflow.getView(); overflowImage.setLayoutParams(new LinearLayout.LayoutParams(dp(36), dp(54))); overflowImage.setPadding(0, 0, dp(16), 0); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java index 361f6bd5..a85fb303 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java @@ -19,17 +19,15 @@ package org.floens.chan.ui.toolbar; import android.content.Context; import android.graphics.drawable.Drawable; -import android.os.Build; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; -import org.floens.chan.R; import org.floens.chan.ui.view.FloatingMenu; import org.floens.chan.ui.view.FloatingMenuItem; import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AndroidUtils.getAttrDrawable; +import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground; public class ToolbarMenuItem implements View.OnClickListener, FloatingMenu.FloatingMenuCallback { private ToolbarMenuItemCallback callback; @@ -63,11 +61,7 @@ public class ToolbarMenuItem implements View.OnClickListener, FloatingMenu.Float imageView.setImageDrawable(drawable); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - imageView.setBackground(getAttrDrawable(context, android.R.attr.selectableItemBackgroundBorderless)); - } else { - imageView.setBackgroundResource(R.drawable.item_background); - } + setRoundItemBackground(imageView); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java b/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java deleted file mode 100644 index e92fd6aa..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.floens.chan.ui.transition; - -import org.floens.chan.controller.ControllerTransition; - -public class ImageTransition extends ControllerTransition { - @Override - public void perform() { - - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/CrossfadeView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/CrossfadeView.java index e1465362..72d03af5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/CrossfadeView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/CrossfadeView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.animation.Animator; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/CustomNetworkImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/CustomNetworkImageView.java deleted file mode 100644 index 224f96cb..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/CustomNetworkImageView.java +++ /dev/null @@ -1,296 +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.floens.chan.ui.view; - -import android.content.Context; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.ViewGroup.LayoutParams; -import android.widget.ImageView; - -import com.android.volley.VolleyError; -import com.android.volley.toolbox.ImageLoader; -import com.android.volley.toolbox.ImageLoader.ImageContainer; -import com.android.volley.toolbox.ImageLoader.ImageListener; - -/** - * Custom version of NetworkImageView - *

- * Handles fetching an image from a URL as well as the life-cycle of the - * associated request. - */ -public class CustomNetworkImageView extends ImageView { - /** - * The URL of the network image to load - */ - private String mUrl; - - /** - * Resource ID of the image to be used as a placeholder until the network - * image is loaded. - */ - private int mDefaultImageId; - - /** - * Resource ID of the image to be used if the network response fails. - */ - private int mErrorImageId; - - /** - * Local copy of the ImageLoader. - */ - private ImageLoader mImageLoader; - - /** - * Current ImageContainer. (either in-flight or finished) - */ - private ImageContainer mImageContainer; - - /** - * Max amount to scale the image inside the view - */ - private float mMaxScale = 1; - - private int mFadeTime; - - private int mForcedWidth = -1; - private int mForcedHeight = -1; - - public CustomNetworkImageView(Context context) { - this(context, null); - } - - public CustomNetworkImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * How larger the inner bitmap is to the defined view size. - * - * @param amount - */ - public void setMaxScale(float amount) { - mMaxScale = amount; - } - - public float getMaxScale() { - return mMaxScale; - } - - public void forceImageDimensions(int w, int h) { - mForcedWidth = w; - mForcedHeight = h; - } - - public String getUrl() { - return mUrl; - } - - /** - * Animate the image fading in. - * - * @param time duration of the fade animation in milliseconds - */ - public void setFadeIn(int time) { - mFadeTime = time; - } - - /** - * Sets URL of the image that should be loaded into this view. Note that - * calling this will immediately either set the cached image (if available) - * or the default image specified by - * {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view. - *

- * NOTE: If applicable, - * {@link CustomNetworkImageView#setDefaultImageResId(int)} and - * {@link CustomNetworkImageView#setErrorImageResId(int)} should be called - * prior to calling this function. - * - * @param url The URL that should be loaded into this ImageView. - * @param imageLoader ImageLoader that will be used to make the request. - */ - public void setImageUrl(String url, ImageLoader imageLoader) { - mUrl = url; - mImageLoader = imageLoader; - // The URL has potentially changed. See if we need to load it. - loadImageIfNecessary(false); - } - - /** - * Sets the default image resource ID to be used for this view until the - * attempt to load it completes. - */ - public void setDefaultImageResId(int defaultImage) { - mDefaultImageId = defaultImage; - } - - /** - * Sets the error image resource ID to be used for this view in the event - * that the image requested fails to load. - */ - public void setErrorImageResId(int errorImage) { - mErrorImageId = errorImage; - } - - public void onErrorResponse(VolleyError error) { - } - - /** - * Loads the image for the view if it isn't already loaded. - * - * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. - */ - void loadImageIfNecessary(final boolean isInLayoutPass) { - int width; - int height; - boolean wrapWidth = false, wrapHeight = false; - - if (mForcedWidth < 0 || mForcedHeight < 0) { - width = getWidth(); - height = getHeight(); - - if (getLayoutParams() != null) { - wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; - wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; - } - - // if the view's bounds aren't known yet, and this is not a - // wrap-content/wrap-content - // view, hold off on loading the image. - boolean isFullyWrapContent = wrapWidth && wrapHeight; - if (width == 0 && height == 0 && !isFullyWrapContent) { - return; - } - } else { - width = mForcedWidth; - height = mForcedHeight; - } - - // if the URL to be loaded in this view is empty, cancel any old - // requests and clear the - // currently loaded image. - if (TextUtils.isEmpty(mUrl)) { - if (mImageContainer != null) { - mImageContainer.cancelRequest(); - mImageContainer = null; - } - setDefaultImageOrNull(); - return; - } - - // if there was an old request in this view, check if it needs to be - // canceled. - if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { - if (mImageContainer.getRequestUrl().equals(mUrl)) { - // if the request is from the same URL, return. - return; - } else { - // if there is a pre-existing request, cancel it if it's - // fetching a different URL. - mImageContainer.cancelRequest(); - setDefaultImageOrNull(); - } - } - - // Calculate the max image width / height to use while ignoring - // WRAP_CONTENT dimens. - int maxWidth = wrapWidth ? 0 : width; - int maxHeight = wrapHeight ? 0 : height; - - // The pre-existing content of this view didn't match the current URL. - // Load the new image - // from the network. - ImageContainer newContainer = mImageLoader.get(mUrl, new ImageListener() { - @Override - public void onErrorResponse(VolleyError error) { - if (mErrorImageId != 0) { - setImageResource(mErrorImageId); - } - - CustomNetworkImageView.this.onErrorResponse(error); - } - - @Override - public void onResponse(final ImageContainer response, boolean isImmediate) { - // If this was an immediate response that was delivered inside of a layout - // pass do not set the image immediately as it will trigger a requestLayout - // inside of a layout. Instead, defer setting the image by posting back to - // the main thread. - if (isImmediate && isInLayoutPass) { - post(new Runnable() { - @Override - public void run() { - onResponse(response, false); - } - }); - return; - } - - if (response.getBitmap() != null) { - setImageBitmap(response.getBitmap()); - - if (mFadeTime > 0 && !isImmediate) { - setAlpha(0f); - animate().alpha(1f).setDuration(mFadeTime); - } - } else if (mDefaultImageId != 0) { - setImageResource(mDefaultImageId); - } - } - }, (int) (maxWidth * mMaxScale), (int) (maxHeight * mMaxScale)); - - // update the ImageContainer to be the new bitmap container. - mImageContainer = newContainer; - } - - private void setDefaultImageOrNull() { - if (mDefaultImageId != 0) { - setImageResource(mDefaultImageId); - } else { - setImageBitmap(null); - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - loadImageIfNecessary(true); - } - - @Override - protected void onDetachedFromWindow() { - if (mImageContainer != null) { - // If the view was bound to an image request, cancel it and clear - // out the image from the view. - mImageContainer.cancelRequest(); - setImageBitmap(null); - // also clear out the container so we can reload the image if - // necessary. - mImageContainer = null; - } - super.onDetachedFromWindow(); - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - invalidate(); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/CustomScaleImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/CustomScaleImageView.java index c9e02cf7..51ab9e81 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/CustomScaleImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/CustomScaleImageView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; @@ -71,6 +88,7 @@ public class CustomScaleImageView extends SubsamplingScaleImageView { public interface Callback { void onReady(); + void onError(boolean wasInitial); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/DragGripView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/DragGripView.java deleted file mode 100644 index 41660c5e..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/DragGripView.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * 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.floens.chan.ui.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; - -public class DragGripView extends View { - private static final int[] ATTRS = new int[]{ - android.R.attr.gravity, - android.R.attr.color, - }; - - private static final int HORIZ_RIDGES = 2; - - private int mGravity = Gravity.START; - private int mColor = 0xff666666; - - private final Paint mRidgePaint; - private final RectF mTempRectF = new RectF(); - - private final float mRidgeSize; - private final float mRidgeGap; - - // private int mWidth; - private int mHeight; - - public DragGripView(Context context) { - this(context, null, 0); - } - - public DragGripView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public DragGripView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); - mGravity = a.getInteger(0, mGravity); - mColor = a.getColor(1, mColor); - a.recycle(); - - final DisplayMetrics res = getResources().getDisplayMetrics(); - mRidgeSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, res); - mRidgeGap = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, res); - - mRidgePaint = new Paint(); - mRidgePaint.setColor(mColor); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension( - View.resolveSize( - (int) (HORIZ_RIDGES * (mRidgeSize + mRidgeGap) - mRidgeGap) - + getPaddingLeft() + getPaddingRight(), - widthMeasureSpec - ), - View.resolveSize( - (int) mRidgeSize, - heightMeasureSpec) - ); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - -// float drawWidth = HORIZ_RIDGES * (mRidgeSize + mRidgeGap) - mRidgeGap; - float drawLeft = getPaddingLeft(); - - int vertRidges = (int) ((mHeight - getPaddingTop() - getPaddingBottom() + mRidgeGap) - / (mRidgeSize + mRidgeGap)); - float drawHeight = vertRidges * (mRidgeSize + mRidgeGap) - mRidgeGap; - float drawTop = getPaddingTop() - + ((mHeight - getPaddingTop() - getPaddingBottom()) - drawHeight) / 2; - - for (int y = 0; y < vertRidges; y++) { - for (int x = 0; x < HORIZ_RIDGES; x++) { - mTempRectF.set( - drawLeft + x * (mRidgeSize + mRidgeGap), - drawTop + y * (mRidgeSize + mRidgeGap), - drawLeft + x * (mRidgeSize + mRidgeGap) + mRidgeSize, - drawTop + y * (mRidgeSize + mRidgeGap) + mRidgeSize); - canvas.drawOval(mTempRectF, mRidgePaint); - } - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - mHeight = h; -// mWidth = w; - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java b/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java index 13393927..c3105fdd 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/FloatingMenu.java @@ -34,7 +34,6 @@ import org.floens.chan.R; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.Logger; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/HackyViewPager.java b/Clover/app/src/main/java/org/floens/chan/ui/view/HackyViewPager.java deleted file mode 100644 index 71b870e1..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/HackyViewPager.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.view; - -import android.content.Context; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.view.MotionEvent; - -/** - * Hacky fix for Issue #4 and - * http://code.google.com/p/android/issues/detail?id=18990 - *

- * ScaleGestureDetector seems to mess up the touch events, which means that - * ViewGroups which make use of onInterceptTouchEvent throw a lot of - * IllegalArgumentException: pointerIndex out of range. - *

- * There's not much I can do in my code for now, but we can mask the result by - * just catching the problem and ignoring it. - * - * @author Chris Banes - */ -public class HackyViewPager extends ViewPager { - - public HackyViewPager(Context context) { - super(context); - } - - public HackyViewPager(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return super.onInterceptTouchEvent(ev); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - return false; - } - } - -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadingBar.java b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadingBar.java index a3af7010..471e5bda 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadingBar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadingBar.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java index aa26bf96..e1dcc1d3 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java @@ -25,7 +25,6 @@ import android.net.Uri; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.MediaController; @@ -36,12 +35,12 @@ import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader.ImageContainer; import com.davemorrissey.labs.subscaleview.ImageSource; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; +import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.model.PostImage; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.FileCache; import org.floens.chan.utils.Logger; import java.io.File; @@ -161,7 +160,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } // Also use volley for the thumbnails - thumbnailRequest = ChanApplication.getVolleyImageLoader().get(thumbnailUrl, new com.android.volley.toolbox.ImageLoader.ImageListener() { + thumbnailRequest = Chan.getVolleyImageLoader().get(thumbnailUrl, new com.android.volley.toolbox.ImageLoader.ImageListener() { @Override public void onErrorResponse(VolleyError error) { thumbnailRequest = null; @@ -188,7 +187,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } callback.showProgress(this, true); - bigImageRequest = ChanApplication.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { + bigImageRequest = Chan.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { callback.onProgress(MultiImageView.this, downloaded, total); @@ -245,7 +244,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } callback.showProgress(this, true); - gifRequest = ChanApplication.getFileCache().downloadFile(gifUrl, new FileCache.DownloadedCallback() { + gifRequest = Chan.getFileCache().downloadFile(gifUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { callback.onProgress(MultiImageView.this, downloaded, total); @@ -296,7 +295,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener public void setVideo(String videoUrl) { callback.showProgress(this, true); - videoRequest = ChanApplication.getFileCache().downloadFile(videoUrl, new FileCache.DownloadedCallback() { + videoRequest = Chan.getFileCache().downloadFile(videoUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { callback.onProgress(MultiImageView.this, downloaded, total); @@ -326,7 +325,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } public void setVideoFile(final File file) { - if (ChanSettings.getVideoExternal()) { + if (ChanSettings.videoOpenExternal.get()) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "video/*"); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java b/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java index ded4fde1..24a5009c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java deleted file mode 100644 index 88f62311..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java +++ /dev/null @@ -1,633 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.view; - -import android.app.Activity; -import android.content.Context; -import android.content.res.TypedArray; -import android.text.Layout; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannedString; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.method.LinkMovementMethod; -import android.text.style.AbsoluteSizeSpan; -import android.text.style.BackgroundColorSpan; -import android.text.style.ClickableSpan; -import android.text.style.ForegroundColorSpan; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.PopupMenu; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.android.volley.toolbox.NetworkImageView; - -import org.floens.chan.ChanApplication; -import org.floens.chan.R; -import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.Post; -import org.floens.chan.core.model.PostLinkable; -import org.floens.chan.utils.IconCache; -import org.floens.chan.utils.ThemeHelper; -import org.floens.chan.utils.Time; - -import static org.floens.chan.utils.AndroidUtils.setItemBackground; - -public class PostView extends LinearLayout implements View.OnClickListener, PostLinkable.Callback { - private final static LinearLayout.LayoutParams matchParams = new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - private final static LinearLayout.LayoutParams wrapParams = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - private final static LinearLayout.LayoutParams matchWrapParams = new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - private final static LinearLayout.LayoutParams wrapMatchParams = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - - private final Activity context; - - private Post post; - private PostViewCallback callback; - private Loadable loadable; - private int highlightQuotesNo = -1; - - private boolean ignoreNextOnClick = false; - - private boolean isBuild = false; - private LinearLayout full; - private LinearLayout contentContainer; - private int imageSize; - private ThumbnailView thumbnailView; - private TextView titleView; - private TextView commentView; - private TextView repliesCountView; - private LinearLayout iconsView; - private ImageView stickyView; - private ImageView closedView; - private ImageView deletedView; - private ImageView archivedView; - private NetworkImageView countryView; - private ImageView optionsView; - private View lastSeen; - - /** - * Represents a post. Use setPost(Post ThreadManager) to fill it with data. - * setPost can be called multiple times (useful for ListView). - * - * @param activity - */ - public PostView(Context activity) { - super(activity); - context = (Activity) activity; - } - - public PostView(Context activity, AttributeSet attbs) { - super(activity, attbs); - context = (Activity) activity; - } - - public PostView(Context activity, AttributeSet attbs, int style) { - super(activity, attbs, style); - context = (Activity) activity; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (this.post != null) { - setPostLinkableListener(null); - } - } - - public void setPost(final Post post, final PostViewCallback callback, boolean highlighted) { - if (this.post != null) { - // Remove callbacks from the old post while it is still set - setPostLinkableListener(null); - } - - this.post = post; - this.callback = callback; - this.loadable = callback.getLoadable(); - - highlightQuotesNo = -1; - setPostLinkableListener(this); - - boolean boardCatalogMode = loadable.isBoardMode() || loadable.isCatalogMode(); - - TypedArray ta = context.obtainStyledAttributes(null, R.styleable.PostView, R.attr.post_style, 0); - - if (!isBuild) { - buildView(context, ta); - isBuild = true; - } - - int dateColor = ta.getColor(R.styleable.PostView_date_color, 0); - int savedReplyColor = ta.getColor(R.styleable.PostView_saved_reply_color, 0); - int highlightedColor = ta.getColor(R.styleable.PostView_highlighted_color, 0); - int detailSize = ta.getDimensionPixelSize(R.styleable.PostView_detail_size, 0); - - ta.recycle(); - - if (post.hasImage) { - thumbnailView.setVisibility(View.VISIBLE); - thumbnailView.setUrl(post.thumbnailUrl, imageSize, imageSize); - } else { - thumbnailView.setVisibility(View.GONE); - thumbnailView.setUrl(null, 0, 0); - } - - CharSequence total = new SpannableString(""); - - if (post.subjectSpan != null) { - total = TextUtils.concat(total, post.subjectSpan); - } - - if (isList()) { - CharSequence relativeTime = DateUtils.getRelativeTimeSpanString(post.time * 1000L, Time.get(), DateUtils.SECOND_IN_MILLIS, 0); - SpannableString date = new SpannableString("No." + post.no + " " + relativeTime); - date.setSpan(new ForegroundColorSpan(dateColor), 0, date.length(), 0); - date.setSpan(new AbsoluteSizeSpan(detailSize), 0, date.length(), 0); - - total = TextUtils.concat(total, post.subjectSpan == null ? "" : "\n", post.nameTripcodeIdCapcodeSpan, date, " "); - } - - if (!TextUtils.isEmpty(total)) { - titleView.setText(total); - titleView.setVisibility(View.VISIBLE); - } else { - titleView.setVisibility(View.GONE); - } - - commentView.setText(post.comment); - - if (loadable.isThreadMode()) { - commentView.setMovementMethod(new PostViewMovementMethod()); - commentView.setOnClickListener(this); - } else { - commentView.setOnClickListener(null); - commentView.setClickable(false); - commentView.setMovementMethod(null); - } - - if (isGrid() || ((post.isOP && boardCatalogMode && post.replies > 0) || (post.repliesFrom.size() > 0))) { - repliesCountView.setVisibility(View.VISIBLE); - - String text = ""; - - int count = boardCatalogMode ? post.replies : post.repliesFrom.size(); - - if (count != 1) { - text = count + " " + context.getString(R.string.multiple_replies); - } else if (count == 1) { - text = count + " " + context.getString(R.string.one_reply); - } - - if (boardCatalogMode && post.images > 0) { - if (post.images != 1) { - text += ", " + post.images + " " + context.getString(R.string.multiple_images); - } else { - text += ", " + post.images + " " + context.getString(R.string.one_image); - } - } - - if (loadable.isThreadMode()) { - repliesCountView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - callback.onShowPostReplies(post); - } - }); - } - - repliesCountView.setText(text); - } else { - repliesCountView.setVisibility(View.GONE); - repliesCountView.setOnClickListener(null); - } - - boolean showCountryFlag = isList() && !TextUtils.isEmpty(post.country) && !TextUtils.isEmpty(post.countryUrl); - boolean showStickyIcon = isList() && post.sticky; - boolean showDeletedIcon = isList() && post.deleted; - boolean showArchivedIcon = isList() && post.archived; - boolean showClosedIcon = isList() && post.closed && !showArchivedIcon; - - iconsView.setVisibility((showCountryFlag || showStickyIcon || showClosedIcon || showDeletedIcon || showArchivedIcon) ? View.VISIBLE : View.GONE); - - stickyView.setVisibility(showStickyIcon ? View.VISIBLE : View.GONE); - closedView.setVisibility(showClosedIcon ? View.VISIBLE : View.GONE); - deletedView.setVisibility(showDeletedIcon ? View.VISIBLE : View.GONE); - archivedView.setVisibility(showArchivedIcon ? View.VISIBLE : View.GONE); - if (showCountryFlag) { - countryView.setVisibility(View.VISIBLE); - countryView.setImageUrl(post.countryUrl, ChanApplication.getVolleyImageLoader()); - } else { - countryView.setVisibility(View.GONE); - countryView.setImageUrl(null, null); - } - - if (post.isSavedReply) { - full.setBackgroundColor(savedReplyColor); - } else if (highlighted) { - full.setBackgroundColor(highlightedColor); - } else { - full.setBackgroundColor(0x00000000); - } - - if (callback.isPostLastSeen(post)) { - lastSeen.setVisibility(View.VISIBLE); - } else { - lastSeen.setVisibility(View.GONE); - } - } - - public Post getPost() { - return post; - } - - public ThumbnailView getThumbnail() { - return thumbnailView; - } - - public void setHighlightQuotesWithNo(int no) { - highlightQuotesNo = no; - } - - private void setPostLinkableListener(PostLinkable.Callback callback) { - if (post.comment instanceof SpannedString) { - SpannedString commentSpannable = (SpannedString) post.comment; - PostLinkable[] linkables = commentSpannable.getSpans(0, commentSpannable.length(), PostLinkable.class); - for (PostLinkable linkable : linkables) { - if (callback == null) { - if (linkable.hasCallback(this)) { - linkable.removeCallback(this); - } - } else { - linkable.addCallback(callback); - } - } - } - } - - private void buildView(final Context context, TypedArray ta) { - int thumbnailBackground = ta.getColor(R.styleable.PostView_thumbnail_background, 0); - int replyCountColor = ta.getColor(R.styleable.PostView_reply_count_color, 0); - - int iconPadding = ta.getDimensionPixelSize(R.styleable.PostView_icon_padding, 0); - int iconWidth = ta.getDimensionPixelSize(R.styleable.PostView_icon_width, 0); - int iconHeight = ta.getDimensionPixelSize(R.styleable.PostView_icon_height, 0); - int gridHeight = ta.getDimensionPixelSize(R.styleable.PostView_grid_height, 0); - int optionsSpacing = ta.getDimensionPixelSize(R.styleable.PostView_options_spacing, 0); - int titleSize = ta.getDimensionPixelSize(R.styleable.PostView_title_size, 0); - int optionsLeftPadding = ta.getDimensionPixelSize(R.styleable.PostView_options_left_padding, 0); - int optionsTopPadding = ta.getDimensionPixelSize(R.styleable.PostView_options_top_padding, 0); - int optionsRightPadding = ta.getDimensionPixelSize(R.styleable.PostView_options_right_padding, 0); - int optionsBottomPadding = ta.getDimensionPixelSize(R.styleable.PostView_options_bottom_padding, 0); - int lastSeenHeight = ta.getDimensionPixelSize(R.styleable.PostView_last_seen_height, 0); - - int postListMaxHeight = ta.getDimensionPixelSize(R.styleable.PostView_list_comment_max_height, 0); - - int postCommentSize = 0; - int commentPadding = 0; - int postPadding = 0; - imageSize = 0; - int repliesCountSize = 0; - if (isList()) { - postCommentSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()); - commentPadding = ta.getDimensionPixelSize(R.styleable.PostView_list_comment_padding, 0); - postPadding = ta.getDimensionPixelSize(R.styleable.PostView_list_padding, 0); - imageSize = ta.getDimensionPixelSize(R.styleable.PostView_list_image_size, 0); - repliesCountSize = ta.getDimensionPixelSize(R.styleable.PostView_list_replies_count_size, 0); - } else if (isGrid()) { - postCommentSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12 - 1, getResources().getDisplayMetrics()); - commentPadding = ta.getDimensionPixelSize(R.styleable.PostView_grid_comment_padding, 0); - postPadding = ta.getDimensionPixelSize(R.styleable.PostView_grid_padding, 0); - imageSize = ta.getDimensionPixelSize(R.styleable.PostView_grid_image_size, 0); - repliesCountSize = ta.getDimensionPixelSize(R.styleable.PostView_grid_replies_count_size, 0); - } - - RelativeLayout wrapper = new RelativeLayout(context); - wrapper.setLayoutParams(matchParams); - - full = new LinearLayout(context); - if (isList()) { - full.setOrientation(HORIZONTAL); - wrapper.addView(full, matchParams); - } else if (isGrid()) { - full.setOrientation(VERTICAL); - wrapper.addView(full, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, gridHeight)); - } - - LinearLayout imageContainer = new LinearLayout(context); - imageContainer.setOrientation(VERTICAL); - imageContainer.setBackgroundColor(thumbnailBackground); - - // Create thumbnail - thumbnailView = new ThumbnailView(context); - thumbnailView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - callback.onThumbnailClicked(post, thumbnailView); - } - }); - - if (isList()) { - imageContainer.addView(thumbnailView, new LinearLayout.LayoutParams(imageSize, imageSize)); - full.addView(imageContainer, wrapMatchParams); - full.setMinimumHeight(imageSize); - } else if (isGrid()) { - imageContainer.addView(thumbnailView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, imageSize)); - full.addView(imageContainer, matchWrapParams); - } - - contentContainer = new LinearLayout(context); - contentContainer.setOrientation(VERTICAL); - - LinearLayout titleContainer = new LinearLayout(context); - titleContainer.setOrientation(HORIZONTAL); - - if (isList()) { - // 25 padding to give optionsView some space - titleContainer.setPadding(0, 0, optionsSpacing, 0); - } - - titleView = new TextView(context); - titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize); - titleView.setPadding(postPadding, postPadding, postPadding, 0); - - titleContainer.addView(titleView, wrapParams); - - contentContainer.addView(titleContainer, matchWrapParams); - - iconsView = new LinearLayout(context); - iconsView.setOrientation(HORIZONTAL); - iconsView.setPadding(postPadding, iconPadding, postPadding, 0); - - stickyView = new ImageView(context); - stickyView.setImageDrawable(IconCache.stickyIcon); - iconsView.addView(stickyView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - - closedView = new ImageView(context); - closedView.setImageDrawable(IconCache.closedIcon); - iconsView.addView(closedView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - - deletedView = new ImageView(context); - deletedView.setImageDrawable(IconCache.trashIcon); - iconsView.addView(deletedView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - - archivedView = new ImageView(context); - archivedView.setImageDrawable(IconCache.archivedIcon); - iconsView.addView(archivedView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - - countryView = new NetworkImageView(context); - countryView.setScaleType(ImageView.ScaleType.FIT_CENTER); - iconsView.addView(countryView, new LinearLayout.LayoutParams(iconWidth, iconHeight)); - - contentContainer.addView(iconsView, matchWrapParams); - - commentView = new TextView(context); - commentView.setTextSize(TypedValue.COMPLEX_UNIT_PX, postCommentSize); - - if (isList()) { - commentView.setPadding(postPadding, commentPadding, postPadding, commentPadding); - - if (loadable.isBoardMode() || loadable.isCatalogMode()) { - commentView.setMaxHeight(postListMaxHeight); - } - } else if (isGrid()) { - commentView.setPadding(postPadding, commentPadding, postPadding, 0); - // So that is fills up all the height using weight later on - commentView.setMinHeight(10000); - } - - if (isList()) { - contentContainer.addView(commentView, matchWrapParams); - } else if (isGrid()) { - contentContainer.addView(commentView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f)); - } - - repliesCountView = new TextView(context); - setItemBackground(repliesCountView); - repliesCountView.setTextColor(replyCountColor); - repliesCountView.setPadding(postPadding, postPadding, postPadding, postPadding); - repliesCountView.setTextSize(TypedValue.COMPLEX_UNIT_PX, repliesCountSize); - repliesCountView.setSingleLine(); - - contentContainer.addView(repliesCountView, wrapParams); - - lastSeen = new View(context); - lastSeen.setBackgroundColor(0xffff0000); - contentContainer.addView(lastSeen, new LayoutParams(LayoutParams.MATCH_PARENT, lastSeenHeight)); - - if (!loadable.isThreadMode()) { - setItemBackground(contentContainer); - } - - full.addView(contentContainer, matchWrapParams); - - optionsView = new ImageView(context); - optionsView.setImageResource(R.drawable.ic_overflow); - setItemBackground(optionsView); - optionsView.setPadding(optionsLeftPadding, optionsTopPadding, optionsRightPadding, optionsBottomPadding); - optionsView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(final View v) { - PopupMenu popupMenu = new PopupMenu(context, v); - callback.onPopulatePostOptions(post, popupMenu.getMenu()); - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - callback.onPostOptionClicked(post, item.getItemId()); - return true; - } - }); - popupMenu.show(); - if (ThemeHelper.getInstance().getTheme().isLightTheme) { - optionsView.setImageResource(R.drawable.ic_overflow_black); - popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() { - @Override - public void onDismiss(final PopupMenu menu) { - optionsView.setImageResource(R.drawable.ic_overflow); - } - }); - } - } - }); - wrapper.addView(optionsView, wrapParams); - RelativeLayout.LayoutParams optionsParams = (RelativeLayout.LayoutParams) optionsView.getLayoutParams(); - optionsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - optionsParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); - optionsView.setLayoutParams(optionsParams); - - addView(wrapper, matchParams); - - wrapper.setOnClickListener(this); - } - - public void setOnClickListeners(final View.OnClickListener listener) { - commentView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (ignoreNextOnClick) { - ignoreNextOnClick = false; - } else { - listener.onClick(v); - } - } - }); - } - - public void onLinkableClick(PostLinkable linkable) { - callback.onPostLinkableClicked(linkable); - } - - @Override - public int getMarkedNo(PostLinkable postLinkable) { - return highlightQuotesNo; - } - - @Override - public void onClick(View v) { - if (ignoreNextOnClick) { - ignoreNextOnClick = false; - } else { - callback.onPostClicked(post); - } - } - - private boolean isList() { - return true; - // TODO -// return callback.getViewMode() == ThreadManager.ViewMode.LIST; - } - - private boolean isGrid() { - return false; - // TODO -// return callback.getViewMode() == ThreadManager.ViewMode.GRID; - } - - public interface PostViewCallback { - Loadable getLoadable(); - - void onPostClicked(Post post); - - void onThumbnailClicked(Post post, ThumbnailView thumbnail); - - void onShowPostReplies(Post post); - - void onPopulatePostOptions(Post post, Menu menu); - - void onPostOptionClicked(Post post, int id); - - void onPostLinkableClicked(PostLinkable linkable); - - boolean isPostLastSeen(Post post); - } - - private static BackgroundColorSpan BACKGROUND_SPAN = new BackgroundColorSpan(0x6633B5E5); - - private class PostViewMovementMethod extends LinkMovementMethod { - @Override - public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - - x += widget.getScrollX(); - y += widget.getScrollY(); - - Layout layout = widget.getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - - ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - ignoreNextOnClick = true; - link[0].onClick(widget); - buffer.removeSpan(BACKGROUND_SPAN); - } else if (action == MotionEvent.ACTION_DOWN) { - buffer.setSpan(BACKGROUND_SPAN, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]), 0); - } else if (action == MotionEvent.ACTION_CANCEL) { - buffer.removeSpan(BACKGROUND_SPAN); - } - - return true; - } else { - buffer.removeSpan(BACKGROUND_SPAN); - } - } - - return true; - } - } - - /*private class PostViewMovementMethod extends LinkMovementMethod { - @Override - public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { - int action = event.getAction(); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { - int x = (int) event.getX(); - int y = (int) event.getY(); - - x -= widget.getTotalPaddingLeft(); - y -= widget.getTotalPaddingTop(); - - x += widget.getScrollX(); - y += widget.getScrollY(); - - Layout layout = widget.getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - - ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); - - if (link.length != 0) { - if (action == MotionEvent.ACTION_UP) { - link[0].onClick(widget); - } - - commentView.invalidate(); - - return true; - } else { - // Changed this to propagate events - PostView.this.onTouchEvent(event); - return true; - } - } else { - PostView.this.onTouchEvent(event); - return true; - } - } - }*/ -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/SelectionListeningEditText.java b/Clover/app/src/main/java/org/floens/chan/ui/view/SelectionListeningEditText.java index 6bbb3a7b..7aa28973 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/SelectionListeningEditText.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/SelectionListeningEditText.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java index f73ddeb4..35b80c46 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; @@ -18,7 +35,7 @@ import com.android.volley.NoConnectionError; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import static org.floens.chan.utils.AndroidUtils.getString; @@ -78,7 +95,7 @@ public class ThumbnailView extends View implements ImageLoader.ImageListener { } if (!TextUtils.isEmpty(url)) { - container = ChanApplication.getVolleyImageLoader().get(url, this, width, height); + container = Chan.getVolleyImageLoader().get(url, this, width, height); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/TouchBlockingFrameLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/view/TouchBlockingFrameLayout.java index da899715..5f08bacb 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/TouchBlockingFrameLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/TouchBlockingFrameLayout.java @@ -1,6 +1,24 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; +import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; @@ -19,7 +37,7 @@ public class TouchBlockingFrameLayout extends FrameLayout { } @Override - public boolean onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(@NonNull MotionEvent event) { return true; } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java index f32eaee8..090a7bf0 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java @@ -1,3 +1,20 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.content.Context; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/ViewPagerAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/view/ViewPagerAdapter.java index 2575f617..4318d76c 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/ViewPagerAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/ViewPagerAdapter.java @@ -1,11 +1,26 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ package org.floens.chan.ui.view; import android.support.v4.view.PagerAdapter; import android.view.View; import android.view.ViewGroup; -import org.floens.chan.utils.AndroidUtils; - public abstract class ViewPagerAdapter extends PagerAdapter { @Override public Object instantiateItem(ViewGroup container, int position) { @@ -18,7 +33,7 @@ public abstract class ViewPagerAdapter extends PagerAdapter { @Override public void destroyItem(ViewGroup container, int position, Object object) { - container.removeView((View)object); + container.removeView((View) object); } public boolean isViewFromObject(View view, Object object) { diff --git a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java index 82f0e231..5271c584 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java @@ -18,6 +18,7 @@ package org.floens.chan.utils; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.Activity; import android.app.Dialog; import android.content.Context; @@ -41,22 +42,15 @@ import android.view.inputmethod.InputMethodManager; import android.webkit.WebView; import android.widget.Toast; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; public class AndroidUtils { - public final static ViewGroup.LayoutParams MATCH_PARAMS = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - public final static ViewGroup.LayoutParams WRAP_PARAMS = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - public final static ViewGroup.LayoutParams MATCH_WRAP_PARAMS = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - public final static ViewGroup.LayoutParams WRAP_MATCH_PARAMS = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); private static HashMap typefaceCache = new HashMap<>(); public static Typeface ROBOTO_MEDIUM; @@ -68,11 +62,11 @@ public class AndroidUtils { } public static Resources getRes() { - return ChanApplication.con.getResources(); + return Chan.con.getResources(); } public static Context getAppRes() { - return ChanApplication.con; + return Chan.con; } public static String getString(int res) { @@ -80,13 +74,13 @@ public class AndroidUtils { } public static SharedPreferences getPreferences() { - return PreferenceManager.getDefaultSharedPreferences(ChanApplication.con); + return PreferenceManager.getDefaultSharedPreferences(Chan.con); } @SuppressLint("SetJavaScriptEnabled") public static void openWebView(Activity activity, String title, String link) { Dialog dialog = new Dialog(activity); - dialog.setContentView(R.layout.web_dialog); + dialog.setContentView(R.layout.dialog_web); WebView wb = (WebView) dialog.findViewById(R.id.web_view); wb.getSettings().setJavaScriptEnabled(true); wb.loadUrl(link); @@ -162,8 +156,6 @@ public class AndroidUtils { /** * Causes the runnable to be added to the message queue. The runnable will * be run on the ui thread. - * - * @param runnable */ public static void runOnUiThread(Runnable runnable) { new Handler(Looper.getMainLooper()).post(runnable); @@ -199,7 +191,7 @@ public class AndroidUtils { return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + return String.format(Locale.US, "%.1f %sB", bytes / Math.pow(unit, exp), pre); } public static CharSequence ellipsize(CharSequence text, int max) { @@ -256,13 +248,9 @@ public class AndroidUtils { } } - public static void setItemBackground(View view) { - view.setBackgroundResource(R.drawable.item_background); - } - public static void setRoundItemBackground(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - view.setBackground(getAttrDrawable(view.getContext(), android.R.attr.selectableItemBackgroundBorderless)); + setRoundItemBackgroundLollipop(view); } else { view.setBackgroundResource(R.drawable.item_background); } @@ -293,4 +281,9 @@ public class AndroidUtils { return false; } } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private static void setRoundItemBackgroundLollipop(View view) { + view.setBackground(getAttrDrawable(view.getContext(), android.R.attr.selectableItemBackgroundBorderless)); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/utils/IconCache.java b/Clover/app/src/main/java/org/floens/chan/utils/IconCache.java deleted file mode 100644 index a59a46ad..00000000 --- a/Clover/app/src/main/java/org/floens/chan/utils/IconCache.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.utils; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; - -import org.floens.chan.R; - -public class IconCache { - public static BitmapDrawable stickyIcon; - public static BitmapDrawable closedIcon; - public static BitmapDrawable trashIcon; - public static BitmapDrawable archivedIcon; - - public static void createIcons(final Context context) { - Resources res = context.getResources(); - stickyIcon = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.sticky_icon)); - closedIcon = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.closed_icon)); - trashIcon = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.trash_icon)); - archivedIcon = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.archived_icon)); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java index 940a29b1..ea361bb7 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java @@ -28,8 +28,9 @@ import android.media.MediaScannerConnection; import android.net.Uri; import android.widget.Toast; -import org.floens.chan.ChanApplication; +import org.floens.chan.Chan; import org.floens.chan.R; +import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.settings.ChanSettings; import java.io.File; @@ -53,9 +54,9 @@ public class ImageSaver { private BroadcastReceiver receiver; public void saveAll(final Context context, String folderName, final List list) { - final File subFolder = new File(ChanSettings.getImageSaveDirectory() + File.separator + filterName(folderName)); + final File subFolder = new File(ChanSettings.saveLocation.get() + File.separator + filterName(folderName)); - String text = context.getString(R.string.download_confirm, Integer.toString(list.size()), subFolder.getAbsolutePath()); + String text = context.getString(R.string.image_download_confirm, Integer.toString(list.size()), subFolder.getAbsolutePath()); new AlertDialog.Builder(context).setMessage(text).setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @@ -67,7 +68,7 @@ public class ImageSaver { } public void saveImage(final Context context, String imageUrl, final String name, final String extension, final boolean share) { - ChanApplication.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { + Chan.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { @Override @SuppressWarnings("deprecation") public void onProgress(long downloaded, long total, boolean done) { @@ -97,7 +98,7 @@ public class ImageSaver { new Thread(new Runnable() { @Override public void run() { - File saveDir = ChanSettings.getImageSaveDirectory(); + File saveDir = new File(ChanSettings.saveLocation.get()); if (!saveDir.isDirectory() && !saveDir.mkdirs()) { showToast(context, context.getString(R.string.image_save_directory_error)); diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java b/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java index 99ccbb15..e69227f8 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/ThemeHelper.java @@ -26,15 +26,15 @@ import org.floens.chan.core.settings.ChanSettings; public class ThemeHelper { public enum Theme { - LIGHT("light", R.style.AppTheme, true), - DARK("dark", R.style.AppTheme_Dark, false), - BLACK("black", R.style.AppTheme_Dark_Black, false); + LIGHT("light", R.style.Chan_Theme, true), + DARK("dark", R.style.Chan_Theme_Dark, false), + BLACK("black", R.style.Chan_Theme_Black, false); public String name; public int resValue; public boolean isLightTheme; - private Theme(String name, int resValue, boolean isLightTheme) { + Theme(String name, int resValue, boolean isLightTheme) { this.name = name; this.resValue = resValue; this.isLightTheme = isLightTheme; @@ -65,7 +65,7 @@ public class ThemeHelper { } public Theme getTheme() { - String themeName = ChanSettings.getTheme(); + String themeName = ChanSettings.theme.get(); Theme theme = null; switch (themeName) { diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment.png deleted file mode 100644 index 175adeb4..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment_dark.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment_dark.png deleted file mode 100644 index 34fd62b1..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_attachment_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_back.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_back.png deleted file mode 100644 index bff21fbd..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_back.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_back_dark.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_back_dark.png deleted file mode 100644 index 6bb7a0b0..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_back_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png deleted file mode 100644 index f889617e..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png deleted file mode 100644 index e206f296..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_cancel_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_done.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_done.png deleted file mode 100644 index 58bf9721..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_done.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_done_dark.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_done_dark.png deleted file mode 100644 index 4c2e8444..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_done_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_new.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_new.png deleted file mode 100644 index d866d616..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_new.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_play.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_play.png deleted file mode 100644 index 9a2147ec..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_play.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_refresh.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_refresh.png deleted file mode 100644 index cd16fdd5..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_refresh.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_action_write.png b/Clover/app/src/main/res/drawable-hdpi/ic_action_write.png deleted file mode 100644 index 3ee3e172..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_action_write.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_black_24dp.png new file mode 100644 index 00000000..da807e99 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png new file mode 100644 index 00000000..a2051cef Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_arrow_back_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark.png b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark.png deleted file mode 100644 index b29d9a21..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_filled.png b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_filled.png deleted file mode 100644 index 66d4223d..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_filled.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_outline_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_outline_white_24dp.png new file mode 100644 index 00000000..8b0c16c2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_white_24dp.png new file mode 100644 index 00000000..9de15c51 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_bookmark_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_create_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_create_white_24dp.png new file mode 100644 index 00000000..d3ff58dc Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_create_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_done_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_done_black_24dp.png new file mode 100644 index 00000000..d4c06072 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_done_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_done_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_done_white_24dp.png new file mode 100644 index 00000000..c278b6c2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_done_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_drawer.png b/Clover/app/src/main/res/drawable-hdpi/ic_drawer.png deleted file mode 100644 index 6614ea4f..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_drawer.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_more.png b/Clover/app/src/main/res/drawable-hdpi/ic_more.png deleted file mode 100644 index fdc4a5ad..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/ic_more.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png new file mode 100644 index 00000000..67f07e47 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_more_vert_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png new file mode 100644 index 00000000..ffa7be93 Binary files /dev/null and b/Clover/app/src/main/res/drawable-hdpi/ic_refresh_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-hdpi/panel_shadow.9.png b/Clover/app/src/main/res/drawable-hdpi/panel_shadow.9.png deleted file mode 100644 index 19ae6630..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/panel_shadow.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/progress_primary_holo_light.9.png b/Clover/app/src/main/res/drawable-hdpi/progress_primary_holo_light.9.png deleted file mode 100644 index 2f76a226..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/progress_primary_holo_light.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/skip_arrow_down.png b/Clover/app/src/main/res/drawable-hdpi/skip_arrow_down.png deleted file mode 100644 index c5112b6a..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/skip_arrow_down.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-hdpi/skip_arrow_up.png b/Clover/app/src/main/res/drawable-hdpi/skip_arrow_up.png deleted file mode 100644 index 2c191b7a..00000000 Binary files a/Clover/app/src/main/res/drawable-hdpi/skip_arrow_up.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment.png deleted file mode 100644 index 7f00efdb..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment_dark.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment_dark.png deleted file mode 100644 index d0d8051a..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_attachment_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_back.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_back.png deleted file mode 100644 index 64634618..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_back.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_back_dark.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_back_dark.png deleted file mode 100644 index 8da54cde..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_back_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png deleted file mode 100644 index e84853e4..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png deleted file mode 100644 index 70e6d2d2..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_cancel_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_done.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_done.png deleted file mode 100644 index cf5fab3a..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_done.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_done_dark.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_done_dark.png deleted file mode 100644 index 41107b87..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_done_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_new.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_new.png deleted file mode 100644 index f17e7980..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_new.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_play.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_play.png deleted file mode 100644 index b94fbe6e..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_play.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_refresh.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_refresh.png deleted file mode 100644 index 235c84f1..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_refresh.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_action_write.png b/Clover/app/src/main/res/drawable-mdpi/ic_action_write.png deleted file mode 100644 index 85cff0b9..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_action_write.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_black_24dp.png new file mode 100644 index 00000000..ad388309 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png new file mode 100644 index 00000000..d571552f Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_arrow_back_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark.png b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark.png deleted file mode 100644 index ec69d41f..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_filled.png b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_filled.png deleted file mode 100644 index 7b4e65d7..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_filled.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_outline_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_outline_white_24dp.png new file mode 100644 index 00000000..98427076 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_white_24dp.png new file mode 100644 index 00000000..84f16627 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_bookmark_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_create_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_create_white_24dp.png new file mode 100644 index 00000000..12b09f1d Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_create_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_done_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_done_black_24dp.png new file mode 100644 index 00000000..5e5e7cf2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_done_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_done_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_done_white_24dp.png new file mode 100644 index 00000000..6d84e143 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_done_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_drawer.png b/Clover/app/src/main/res/drawable-mdpi/ic_drawer.png deleted file mode 100644 index b05c026c..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_drawer.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_more.png b/Clover/app/src/main/res/drawable-mdpi/ic_more.png deleted file mode 100644 index 1d8ad18a..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/ic_more.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png new file mode 100644 index 00000000..017e45ed Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_more_vert_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png new file mode 100644 index 00000000..97e42b52 Binary files /dev/null and b/Clover/app/src/main/res/drawable-mdpi/ic_refresh_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-mdpi/panel_shadow.9.png b/Clover/app/src/main/res/drawable-mdpi/panel_shadow.9.png deleted file mode 100644 index e104117f..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/panel_shadow.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/progress_primary_holo_light.9.png b/Clover/app/src/main/res/drawable-mdpi/progress_primary_holo_light.9.png deleted file mode 100644 index 6fb94457..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/progress_primary_holo_light.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/skip_arrow_down.png b/Clover/app/src/main/res/drawable-mdpi/skip_arrow_down.png deleted file mode 100644 index 3fc47b1b..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/skip_arrow_down.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-mdpi/skip_arrow_up.png b/Clover/app/src/main/res/drawable-mdpi/skip_arrow_up.png deleted file mode 100644 index d98671e0..00000000 Binary files a/Clover/app/src/main/res/drawable-mdpi/skip_arrow_up.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment.png deleted file mode 100644 index 860dd0ad..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment_dark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment_dark.png deleted file mode 100644 index e01e86a8..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_attachment_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_back.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_back.png deleted file mode 100644 index 28432f10..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_back.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_back_dark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_back_dark.png deleted file mode 100644 index d5760406..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_back_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png deleted file mode 100644 index 58e2e3b4..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png deleted file mode 100644 index d1634205..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_cancel_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_done.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_done.png deleted file mode 100644 index b8915716..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_done.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_done_dark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_done_dark.png deleted file mode 100644 index 6ee32b64..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_done_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_new.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_new.png deleted file mode 100644 index dde2141f..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_new.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_play.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_play.png deleted file mode 100644 index e01a8445..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_play.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_refresh.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_refresh.png deleted file mode 100644 index 5f89fc25..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_refresh.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_action_write.png b/Clover/app/src/main/res/drawable-xhdpi/ic_action_write.png deleted file mode 100644 index 7f0ea51b..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_action_write.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_black_24dp.png new file mode 100644 index 00000000..68427253 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png new file mode 100644 index 00000000..ce5b878b Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_arrow_back_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark.png b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark.png deleted file mode 100644 index f9f17801..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_filled.png b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_filled.png deleted file mode 100644 index c5698328..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_filled.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_outline_white_24dp.png new file mode 100644 index 00000000..e42e09fa Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_white_24dp.png new file mode 100644 index 00000000..872349cc Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_bookmark_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png new file mode 100644 index 00000000..5a06bff5 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_done_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_done_black_24dp.png new file mode 100644 index 00000000..64a4944f Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_done_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png new file mode 100644 index 00000000..3b2b65d2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_drawer.png b/Clover/app/src/main/res/drawable-xhdpi/ic_drawer.png deleted file mode 100644 index bcf49dd7..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_drawer.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_more.png b/Clover/app/src/main/res/drawable-xhdpi/ic_more.png deleted file mode 100644 index 1b04eda0..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/ic_more.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png new file mode 100644 index 00000000..efab8a74 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_more_vert_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png new file mode 100644 index 00000000..1989184b Binary files /dev/null and b/Clover/app/src/main/res/drawable-xhdpi/ic_refresh_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/panel_shadow.9.png b/Clover/app/src/main/res/drawable-xhdpi/panel_shadow.9.png deleted file mode 100644 index a702c667..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/panel_shadow.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/progress_primary_holo_light.9.png b/Clover/app/src/main/res/drawable-xhdpi/progress_primary_holo_light.9.png deleted file mode 100644 index e62123c4..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/progress_primary_holo_light.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_down.png b/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_down.png deleted file mode 100644 index b8105c86..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_down.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_up.png b/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_up.png deleted file mode 100644 index 5d40a66d..00000000 Binary files a/Clover/app/src/main/res/drawable-xhdpi/skip_arrow_up.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment.png deleted file mode 100644 index 0c5e44fe..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment_dark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment_dark.png deleted file mode 100644 index 305bc41a..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_attachment_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back.png deleted file mode 100644 index 11eb5e19..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back_dark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back_dark.png deleted file mode 100644 index 4919a8d7..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_back_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png deleted file mode 100644 index a9bbcde5..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png deleted file mode 100644 index 5dc21435..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_cancel_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done.png deleted file mode 100644 index 6fda89ec..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done_dark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done_dark.png deleted file mode 100644 index 68c41dec..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_done_dark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_new.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_new.png deleted file mode 100644 index c42c2bfb..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_new.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_play.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_play.png deleted file mode 100644 index 7f7ca2e0..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_play.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png deleted file mode 100644 index 72128fe6..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_write.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_action_write.png deleted file mode 100644 index 34ec7092..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_action_write.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_black_24dp.png new file mode 100644 index 00000000..fa432c23 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png new file mode 100644 index 00000000..746d7757 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark.png deleted file mode 100644 index fedb1471..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_filled.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_filled.png deleted file mode 100644 index abda33a9..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_filled.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_outline_white_24dp.png new file mode 100644 index 00000000..6ed27a24 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_white_24dp.png new file mode 100644 index 00000000..3faff90b Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_bookmark_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png new file mode 100644 index 00000000..02e19d04 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_done_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_done_black_24dp.png new file mode 100644 index 00000000..c9c01741 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_done_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png new file mode 100644 index 00000000..0ebb5555 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_drawer.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_drawer.png deleted file mode 100644 index f7e3b307..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_drawer.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_more.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_more.png deleted file mode 100644 index 2955c02e..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/ic_more.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png new file mode 100644 index 00000000..d3228130 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png new file mode 100644 index 00000000..1692d8a2 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxhdpi/ic_refresh_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/panel_shadow.9.png b/Clover/app/src/main/res/drawable-xxhdpi/panel_shadow.9.png deleted file mode 100644 index 82091c3b..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/panel_shadow.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/progress_primary_holo_light.9.png b/Clover/app/src/main/res/drawable-xxhdpi/progress_primary_holo_light.9.png deleted file mode 100644 index add4d383..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/progress_primary_holo_light.9.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_down.png b/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_down.png deleted file mode 100644 index de4a161d..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_down.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_up.png b/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_up.png deleted file mode 100644 index 66d147a0..00000000 Binary files a/Clover/app/src/main/res/drawable-xxhdpi/skip_arrow_up.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png deleted file mode 100644 index d271d8e0..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_refresh.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_write.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_write.png deleted file mode 100644 index 9380370f..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/ic_action_write.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_black_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_black_24dp.png new file mode 100644 index 00000000..fb5235fb Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png new file mode 100644 index 00000000..fb06e1d4 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark.png deleted file mode 100644 index c987a705..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_filled.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_filled.png deleted file mode 100644 index 325c7b2f..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_filled.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_outline_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_outline_white_24dp.png new file mode 100644 index 00000000..99db20fe Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_outline_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_white_24dp.png new file mode 100644 index 00000000..370cf8af Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_bookmark_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_create_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_create_white_24dp.png new file mode 100644 index 00000000..d6668a05 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_create_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_black_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_black_24dp.png new file mode 100644 index 00000000..2f6d6386 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_black_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png new file mode 100644 index 00000000..d670618c Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_more.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_more.png deleted file mode 100644 index 25b6f9f1..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/ic_more.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png new file mode 100644 index 00000000..2f2cb3d0 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png new file mode 100644 index 00000000..f5beca25 Binary files /dev/null and b/Clover/app/src/main/res/drawable-xxxhdpi/ic_refresh_white_24dp.png differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_down.png b/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_down.png deleted file mode 100644 index 2a3d6b4a..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_down.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_up.png b/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_up.png deleted file mode 100644 index 81db297e..00000000 Binary files a/Clover/app/src/main/res/drawable-xxxhdpi/skip_arrow_up.png and /dev/null differ diff --git a/Clover/app/src/main/res/drawable/pin_icon_blue.xml b/Clover/app/src/main/res/drawable/pin_icon_blue.xml deleted file mode 100644 index b52ecf8e..00000000 --- a/Clover/app/src/main/res/drawable/pin_icon_blue.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/Clover/app/src/main/res/drawable/pin_icon_gray.xml b/Clover/app/src/main/res/drawable/pin_icon_gray.xml deleted file mode 100644 index a94592e4..00000000 --- a/Clover/app/src/main/res/drawable/pin_icon_gray.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/Clover/app/src/main/res/drawable/pin_icon_red.xml b/Clover/app/src/main/res/drawable/pin_icon_red.xml deleted file mode 100644 index d3867dc6..00000000 --- a/Clover/app/src/main/res/drawable/pin_icon_red.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/Clover/app/src/main/res/drawable/preference_description_bottom.xml b/Clover/app/src/main/res/drawable/preference_description_bottom.xml deleted file mode 100644 index 856fffa2..00000000 --- a/Clover/app/src/main/res/drawable/preference_description_bottom.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/Clover/app/src/main/res/drawable/preference_description_top.xml b/Clover/app/src/main/res/drawable/preference_description_top.xml deleted file mode 100644 index 1944ff95..00000000 --- a/Clover/app/src/main/res/drawable/preference_description_top.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/Clover/app/src/main/res/drawable/progressbar_no_bg.xml b/Clover/app/src/main/res/drawable/progressbar_no_bg.xml deleted file mode 100644 index 18952ca4..00000000 --- a/Clover/app/src/main/res/drawable/progressbar_no_bg.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/Clover/app/src/main/res/anim/fade_out.xml b/Clover/app/src/main/res/drawable/setting_description_bottom.xml similarity index 79% rename from Clover/app/src/main/res/anim/fade_out.xml rename to Clover/app/src/main/res/drawable/setting_description_bottom.xml index 3994dfc1..5c2cc907 100644 --- a/Clover/app/src/main/res/anim/fade_out.xml +++ b/Clover/app/src/main/res/drawable/setting_description_bottom.xml @@ -15,9 +15,7 @@ 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 . --> - - - + + + + diff --git a/Clover/app/src/main/res/anim/fade_in.xml b/Clover/app/src/main/res/drawable/setting_description_top.xml similarity index 79% rename from Clover/app/src/main/res/anim/fade_in.xml rename to Clover/app/src/main/res/drawable/setting_description_top.xml index 9c9c27a0..30e597b3 100644 --- a/Clover/app/src/main/res/anim/fade_in.xml +++ b/Clover/app/src/main/res/drawable/setting_description_top.xml @@ -15,9 +15,7 @@ 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 . --> - - - + + + + diff --git a/Clover/app/src/main/res/layout/activity_base.xml b/Clover/app/src/main/res/layout/activity_base.xml deleted file mode 100644 index b88b8375..00000000 --- a/Clover/app/src/main/res/layout/activity_base.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Clover/app/src/main/res/layout/board_edit_item.xml b/Clover/app/src/main/res/layout/board_edit_item.xml deleted file mode 100644 index 6ca3f76e..00000000 --- a/Clover/app/src/main/res/layout/board_edit_item.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - diff --git a/Clover/app/src/main/res/layout/board_select_add.xml b/Clover/app/src/main/res/layout/board_select_add.xml deleted file mode 100644 index 8cefcd90..00000000 --- a/Clover/app/src/main/res/layout/board_select_add.xml +++ /dev/null @@ -1,30 +0,0 @@ - - diff --git a/Clover/app/src/main/res/layout/board_select_spinner.xml b/Clover/app/src/main/res/layout/board_select_spinner.xml deleted file mode 100644 index 69aa7840..00000000 --- a/Clover/app/src/main/res/layout/board_select_spinner.xml +++ /dev/null @@ -1,29 +0,0 @@ - - diff --git a/Clover/app/src/main/res/layout/board_select_spinner_dropdown.xml b/Clover/app/src/main/res/layout/board_select_spinner_dropdown.xml deleted file mode 100644 index 525e726e..00000000 --- a/Clover/app/src/main/res/layout/board_select_spinner_dropdown.xml +++ /dev/null @@ -1,29 +0,0 @@ - - diff --git a/Clover/app/src/main/res/layout/cell_board_edit.xml b/Clover/app/src/main/res/layout/cell_board_edit.xml index f5b1d475..98eb674c 100644 --- a/Clover/app/src/main/res/layout/cell_board_edit.xml +++ b/Clover/app/src/main/res/layout/cell_board_edit.xml @@ -1,18 +1,36 @@ - + + android:background="?attr/selectableItemBackground" + android:orientation="horizontal"> + tools:ignore="ContentDescription" /> + + android:background="?attr/selectableItemBackground" + android:orientation="horizontal"> + + android:layout_height="48dp" + android:orientation="horizontal"> + tools:ignore="ContentDescription" /> diff --git a/Clover/app/src/main/res/layout/cell_link.xml b/Clover/app/src/main/res/layout/cell_link.xml index 5aede947..4711eeb4 100644 --- a/Clover/app/src/main/res/layout/cell_link.xml +++ b/Clover/app/src/main/res/layout/cell_link.xml @@ -1,18 +1,36 @@ - + + android:background="?attr/selectableItemBackground" + android:orientation="horizontal"> + tools:ignore="ContentDescription" /> + + android:background="?android:attr/selectableItemBackground" + android:orientation="horizontal"> + android:paddingTop="4dp" /> diff --git a/Clover/app/src/main/res/layout/cell_post.xml b/Clover/app/src/main/res/layout/cell_post.xml index 2752ebf3..31105d38 100644 --- a/Clover/app/src/main/res/layout/cell_post.xml +++ b/Clover/app/src/main/res/layout/cell_post.xml @@ -1,5 +1,22 @@ - + @@ -75,6 +92,7 @@ android:paddingLeft="15dp" android:paddingRight="5dp" android:paddingTop="5dp" - android:src="?post_options_drawable" /> + android:src="?post_options_drawable" + tools:ignore="ContentDescription" /> diff --git a/Clover/app/src/main/res/layout/cell_thread_status.xml b/Clover/app/src/main/res/layout/cell_thread_status.xml index 99cd5a3f..02ac1d40 100644 --- a/Clover/app/src/main/res/layout/cell_thread_status.xml +++ b/Clover/app/src/main/res/layout/cell_thread_status.xml @@ -1,4 +1,20 @@ - + + android:layout_height="match_parent" + android:gravity="center" + android:textColor="#ff757575" /> diff --git a/Clover/app/src/main/res/layout/controller_board_edit.xml b/Clover/app/src/main/res/layout/controller_board_edit.xml index 9ea8948e..51e68433 100644 --- a/Clover/app/src/main/res/layout/controller_board_edit.xml +++ b/Clover/app/src/main/res/layout/controller_board_edit.xml @@ -1,17 +1,33 @@ - + + android:background="#ffffffff" + android:orientation="vertical"> + android:scrollbars="vertical" /> diff --git a/Clover/app/src/main/res/layout/controller_image_viewer.xml b/Clover/app/src/main/res/layout/controller_image_viewer.xml index 9d0daabc..469b02e7 100644 --- a/Clover/app/src/main/res/layout/controller_image_viewer.xml +++ b/Clover/app/src/main/res/layout/controller_image_viewer.xml @@ -1,4 +1,20 @@ - + @@ -10,14 +26,14 @@ + android:layout_height="match_parent" + android:visibility="invisible" /> + android:layout_height="3dp" + android:visibility="gone" /> diff --git a/Clover/app/src/main/res/layout/controller_navigation_drawer.xml b/Clover/app/src/main/res/layout/controller_navigation_drawer.xml index b54672d6..fc9ce774 100644 --- a/Clover/app/src/main/res/layout/controller_navigation_drawer.xml +++ b/Clover/app/src/main/res/layout/controller_navigation_drawer.xml @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> @@ -32,7 +33,8 @@ along with this program. If not, see . android:layout_width="match_parent" android:layout_height="56dp" android:background="@color/primary" - android:elevation="4dp" /> + android:elevation="4dp" + tools:ignore="UnusedAttribute" /> . + android:scrollbars="vertical" /> diff --git a/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml b/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml index fc8ded08..8db44f0b 100644 --- a/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml +++ b/Clover/app/src/main/res/layout/controller_navigation_image_viewer.xml @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . --> . android:layout_width="match_parent" android:layout_height="56dp" android:background="#e9000000" - android:elevation="4dp" /> + android:elevation="4dp" + tools:ignore="UnusedAttribute" /> + + android:layout_height="match_parent" + android:orientation="vertical"> + android:paddingTop="24dp" + android:text="@string/video_playback_warning" + android:textSize="16sp" /> + android:layout_marginTop="16dp" + android:text="@string/video_playback_ignore" /> diff --git a/Clover/app/src/main/res/layout/watch_description.xml b/Clover/app/src/main/res/layout/dialog_web.xml similarity index 84% rename from Clover/app/src/main/res/layout/watch_description.xml rename to Clover/app/src/main/res/layout/dialog_web.xml index bacd7685..fda6df1d 100644 --- a/Clover/app/src/main/res/layout/watch_description.xml +++ b/Clover/app/src/main/res/layout/dialog_web.xml @@ -18,15 +18,13 @@ along with this program. If not, see . + android:paddingTop="16dp"> - + android:scrollbars="vertical" /> diff --git a/Clover/app/src/main/res/layout/folder_pick.xml b/Clover/app/src/main/res/layout/fragment_folder_pick.xml similarity index 93% rename from Clover/app/src/main/res/layout/folder_pick.xml rename to Clover/app/src/main/res/layout/fragment_folder_pick.xml index c201752b..43ce76b2 100644 --- a/Clover/app/src/main/res/layout/folder_pick.xml +++ b/Clover/app/src/main/res/layout/fragment_folder_pick.xml @@ -44,7 +44,7 @@ along with this program. If not, see . android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:drawableLeft="@drawable/ic_action_cancel" + android:drawableLeft="@drawable/ic_arrow_back_black_24dp" android:drawablePadding="8dp" android:gravity="center_vertical" android:paddingRight="20dp" @@ -64,15 +64,15 @@ along with this program. If not, see . android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:drawableLeft="@drawable/ic_action_done" + android:drawableLeft="@drawable/ic_done_black_24dp" android:drawablePadding="8dp" android:gravity="center_vertical" android:paddingRight="20dp" - android:text="@string/folder_pick_ok" /> + android:text="@string/setting_folder_pick_ok" /> - - - - - - - - - - - - - - - - diff --git a/Clover/app/src/main/res/layout/image_view.xml b/Clover/app/src/main/res/layout/image_view.xml deleted file mode 100644 index 32568011..00000000 --- a/Clover/app/src/main/res/layout/image_view.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - diff --git a/Clover/app/src/main/res/layout/layout_reply.xml b/Clover/app/src/main/res/layout/layout_reply.xml deleted file mode 100644 index 1ef45416..00000000 --- a/Clover/app/src/main/res/layout/layout_reply.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/Clover/app/src/main/res/layout/layout_reply_input.xml b/Clover/app/src/main/res/layout/layout_reply_input.xml index 440af286..418dab17 100644 --- a/Clover/app/src/main/res/layout/layout_reply_input.xml +++ b/Clover/app/src/main/res/layout/layout_reply_input.xml @@ -1,4 +1,20 @@ - + + + android:layout_marginBottom="16dp" + android:textSize="24sp" />