diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt index c422ebb7a..897c7406b 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt @@ -449,8 +449,7 @@ object EhEngine { if (filter) list.removeAll { EhFilter.filterUploader(it) || EhFilter.filterTag(it) || EhFilter.filterTagNamespace(it) } } - suspend fun addFavoritesRange(gidArray: LongArray, tokenArray: Array, dstCat: Int) { - require(gidArray.size == tokenArray.size) - gidArray.forEachIndexed { index, gid -> addFavorites(gid, tokenArray[index], dstCat, null) } + suspend fun addFavoritesRange(gidTokenArray: Array>, dstCat: Int) { + gidTokenArray.forEach { (gid, token) -> addFavorites(gid, token, dstCat, null) } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt b/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt index 6227afb01..e2a040369 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/legacy/EasyRecyclerView.kt @@ -13,423 +13,372 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.hippo.ehviewer.ui.legacy -package com.hippo.ehviewer.ui.legacy; - -import android.content.Context; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.util.SparseBooleanArray; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Checkable; - -import androidx.annotation.NonNull; -import androidx.collection.LongSparseArray; -import androidx.recyclerview.widget.RecyclerView; - -import com.hippo.ehviewer.yorozuya.NumberUtils; +import android.content.Context +import android.os.Parcel +import android.os.Parcelable +import android.util.AttributeSet +import android.util.SparseBooleanArray +import android.view.ActionMode +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Checkable +import androidx.collection.LongSparseArray +import androidx.core.os.ParcelCompat.readParcelable +import androidx.recyclerview.widget.RecyclerView +import com.hippo.ehviewer.yorozuya.NumberUtils.boolean2int +import com.hippo.ehviewer.yorozuya.NumberUtils.int2boolean /** * Add setChoiceMode for RecyclerView */ // Get some code from twoway-view and AbsListView. -public class EasyRecyclerView extends RecyclerView { - - /** - * Normal list that does not indicate choices - */ - public static final int CHOICE_MODE_NONE = 0; - - /** - * The list allows up to one choice - */ - public static final int CHOICE_MODE_SINGLE = 1; - - /** - * The list allows multiple choices - */ - public static final int CHOICE_MODE_MULTIPLE = 2; - - /** - * The list allows multiple choices in a modal selection mode - */ - public static final int CHOICE_MODE_MULTIPLE_MODAL = 3; - - /** - * The list allows multiple choices in custom action - */ - public static final int CHOICE_MODE_MULTIPLE_CUSTOM = 4; +open class EasyRecyclerView : RecyclerView { // Fling friction - public float pageScrollThreshold; + var pageScrollThreshold = 0f + /** * Wrapper for the multiple choice mode callback; AbsListView needs to perform * a few extra actions around what application code does. */ - MultiChoiceModeWrapper mMultiChoiceModeCallback; + var mMultiChoiceModeCallback: MultiChoiceModeWrapper? = null + /** * Controls if/how the user may choose/check items in the list */ - private int mChoiceMode = CHOICE_MODE_NONE; + private var mChoiceMode = CHOICE_MODE_NONE + /** * Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive. */ - private ActionMode mChoiceActionMode; + private var mChoiceActionMode: ActionMode? = null + /** * Listener for custom multiple choices */ - private CustomChoiceListener mCustomChoiceListener; - - private boolean mCustomChoice = false; + private var mCustomChoiceListener: CustomChoiceListener? = null + var isInCustomChoice = false + private set /** * A lock, avoid OutOfCustomChoiceMode when doing OutOfCustomChoiceMode */ - private boolean mOutOfCustomChoiceModing = false; - - private SparseBooleanArray mTempCheckStates; + private var mOutOfCustomChoiceModing = false + private var mTempCheckStates: SparseBooleanArray? = null /** * Running count of how many items are currently checked */ - private int mCheckedItemCount; + var checkedItemCount = 0 + private set /** * Running state of which positions are currently checked */ - private SparseBooleanArray mCheckStates; + private var mCheckStates: SparseBooleanArray? = null /** * Running state of which IDs are currently checked. * If there is a value for a given key, the checked state for that ID is true * and the value holds the last known position in the adapter for that id. */ - private LongSparseArray mCheckedIdStates; + private var mCheckedIdStates: LongSparseArray? = null + private var mAdapter: Adapter<*>? = null - private Adapter mAdapter; - - public EasyRecyclerView(Context context) { - super(context); - EasyRecyclerView.this.init(context); + constructor(context: Context) : super(context) { + init(context) } - public EasyRecyclerView(Context context, AttributeSet attrs) { - super(context, attrs); - EasyRecyclerView.this.init(context); + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + init(context) } - public EasyRecyclerView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - EasyRecyclerView.this.init(context); - } - - public static void setViewChecked(View view, boolean checked) { - if (view instanceof Checkable) { - ((Checkable) view).setChecked(checked); - } else { - view.setActivated(checked); - } + constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super( + context, + attrs, + defStyle + ) { + init(context) } /** * {@inheritDoc} */ - @Override - public void setAdapter(RecyclerView.Adapter adapter) { - - super.setAdapter(adapter); - - mAdapter = adapter; + override fun setAdapter(adapter: Adapter<*>?) { + super.setAdapter(adapter) + mAdapter = adapter if (adapter != null) { - if (mChoiceMode != CHOICE_MODE_NONE && adapter.hasStableIds() && - mCheckedIdStates == null) { - mCheckedIdStates = new LongSparseArray<>(); + if (mChoiceMode != CHOICE_MODE_NONE && adapter.hasStableIds() && mCheckedIdStates == null) { + mCheckedIdStates = LongSparseArray() } } - if (mCheckStates != null) { - mCheckStates.clear(); + mCheckStates!!.clear() } - if (mCheckedIdStates != null) { - mCheckedIdStates.clear(); + mCheckedIdStates!!.clear() } } - @Override - public void onChildAttachedToWindow(@NonNull View child) { - super.onChildAttachedToWindow(child); - + override fun onChildAttachedToWindow(child: View) { + super.onChildAttachedToWindow(child) if (mCheckStates != null) { - int position = getChildAdapterPosition(child); + val position = getChildAdapterPosition(child) if (position >= 0) { - setViewChecked(child, mCheckStates.get(position)); + setViewChecked(child, mCheckStates!![position]) } } } - - public int getCheckedItemCount() { - return mCheckedItemCount; - } - /** * Returns the checked state of the specified position. The result is only - * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE} - * or {@link #CHOICE_MODE_MULTIPLE}. + * valid if the choice mode has been set to [.CHOICE_MODE_SINGLE] + * or [.CHOICE_MODE_MULTIPLE]. * * @param position The item whose checked state to return - * @return The item's checked state or false if choice mode + * @return The item's checked state or `false` if choice mode * is invalid - * @see #setChoiceMode(int) + * @see .setChoiceMode */ - public boolean isItemChecked(int position) { - //noinspection SimplifiableIfStatement - if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { - return mCheckStates.get(position); - } - - return false; + fun isItemChecked(position: Int): Boolean { + return if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { + mCheckStates!![position] + } else false } - /** - * Returns the set of checked items in the list. The result is only valid if - * the choice mode has not been set to {@link #CHOICE_MODE_NONE}. - * - * @return A SparseBooleanArray which will return true for each call to - * get(int position) where position is a checked position in the - * list and false otherwise, or null if the choice - * mode is set to {@link #CHOICE_MODE_NONE}. - */ - public SparseBooleanArray getCheckedItemPositions() { - if (mChoiceMode != CHOICE_MODE_NONE) { - return mCheckStates; - } - return null; - } + val checkedItemPositions: SparseBooleanArray? + /** + * Returns the set of checked items in the list. The result is only valid if + * the choice mode has not been set to [.CHOICE_MODE_NONE]. + * + * @return A SparseBooleanArray which will return true for each call to + * get(int position) where position is a checked position in the + * list and false otherwise, or `null` if the choice + * mode is set to [.CHOICE_MODE_NONE]. + */ + get() = if (mChoiceMode != CHOICE_MODE_NONE) { + mCheckStates + } else null - public boolean isInCustomChoice() { - return mCustomChoice; - } - - public void intoCustomChoiceMode() { - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) { - mCustomChoice = true; - - mCustomChoiceListener.onIntoCustomChoice(this); + fun intoCustomChoiceMode() { + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice) { + isInCustomChoice = true + mCustomChoiceListener!!.onIntoCustomChoice(this) } } - public void outOfCustomChoiceMode() { - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice && !mOutOfCustomChoiceModing) { - mOutOfCustomChoiceModing = true; + fun outOfCustomChoiceMode() { + if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && isInCustomChoice && !mOutOfCustomChoiceModing) { + mOutOfCustomChoiceModing = true // Copy mCheckStates - mTempCheckStates.clear(); - for (int i = 0, n = mCheckStates.size(); i < n; i++) { - mTempCheckStates.put(mCheckStates.keyAt(i), mCheckStates.valueAt(i)); - } - // Uncheck remain checked items - for (int i = 0, n = mTempCheckStates.size(); i < n; i++) { - if (mTempCheckStates.valueAt(i)) { - setItemChecked(mTempCheckStates.keyAt(i), false); + mTempCheckStates!!.clear() + run { + for (i in 0 until mCheckStates!!.size()) { + mTempCheckStates!!.put(mCheckStates!!.keyAt(i), mCheckStates!!.valueAt(i)) } } - - mCustomChoice = false; - mCustomChoiceListener.onOutOfCustomChoice(this); - - mOutOfCustomChoiceModing = false; + // Uncheck remain checked items + for (i in 0 until mTempCheckStates!!.size()) { + if (mTempCheckStates!!.valueAt(i)) { + setItemChecked(mTempCheckStates!!.keyAt(i), false) + } + } + isInCustomChoice = false + mCustomChoiceListener!!.onOutOfCustomChoice(this) + mOutOfCustomChoiceModing = false } } /** * Clear any choices previously set */ - private void clearChoices() { + private fun clearChoices() { if (mCheckStates != null) { - mCheckStates.clear(); + mCheckStates!!.clear() } if (mCheckedIdStates != null) { - mCheckedIdStates.clear(); + mCheckedIdStates!!.clear() } - mCheckedItemCount = 0; - updateOnScreenCheckedViews(); + checkedItemCount = 0 + updateOnScreenCheckedViews() } /** * Sets all items checked. */ - public void checkAll() { + fun checkAll() { if (mChoiceMode == CHOICE_MODE_NONE || mChoiceMode == CHOICE_MODE_SINGLE) { - return; + return } // Check is intoCheckMode - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) { - throw new IllegalStateException("Call intoCheckMode first"); - } + check(!(mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice)) { "Call intoCheckMode first" } // Start selection mode if needed. We don't need to if we're unchecking something. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) { - if (mMultiChoiceModeCallback == null || - !mMultiChoiceModeCallback.hasWrappedCallback()) { - throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " + + check( + !(mMultiChoiceModeCallback == null || + !mMultiChoiceModeCallback!!.hasWrappedCallback()) + ) { + "EasyRecyclerView: attempted to start selection mode " + "for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " + - "supplied. Call setMultiChoiceModeListener to set a callback."); + "supplied. Call setMultiChoiceModeListener to set a callback." } - mChoiceActionMode = startActionMode(mMultiChoiceModeCallback); + mChoiceActionMode = startActionMode(mMultiChoiceModeCallback) } - - for (int i = 0, n = mAdapter.getItemCount(); i < n; i++) { - boolean oldValue = mCheckStates.get(i); - mCheckStates.put(i, true); - if (mCheckedIdStates != null && mAdapter.hasStableIds()) { - mCheckedIdStates.put(mAdapter.getItemId(i), i); + for (i in 0 until mAdapter!!.itemCount) { + val oldValue = mCheckStates!![i] + mCheckStates!!.put(i, true) + if (mCheckedIdStates != null && mAdapter!!.hasStableIds()) { + mCheckedIdStates!!.put(mAdapter!!.getItemId(i), i) } if (!oldValue) { - mCheckedItemCount++; + checkedItemCount++ } if (mChoiceActionMode != null) { - final long id = mAdapter.getItemId(i); - mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, i, id, true); + val id = mAdapter!!.getItemId(i) + mMultiChoiceModeCallback!!.onItemCheckedStateChanged( + mChoiceActionMode!!, + i, + id, + true + ) } if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - final long id = mAdapter.getItemId(i); - mCustomChoiceListener.onItemCheckedStateChanged(this, i, id, true); + val id = mAdapter!!.getItemId(i) + mCustomChoiceListener!!.onItemCheckedStateChanged(this, i, id, true) } } - - updateOnScreenCheckedViews(); + updateOnScreenCheckedViews() } - public void toggleItemChecked(int position) { + fun toggleItemChecked(position: Int) { if (mCheckStates != null) { - setItemChecked(position, !mCheckStates.get(position)); + setItemChecked(position, !mCheckStates!![position]) } } /** * Sets the checked state of the specified position. The is only valid if - * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or - * {@link #CHOICE_MODE_MULTIPLE}. + * the choice mode has been set to [.CHOICE_MODE_SINGLE] or + * [.CHOICE_MODE_MULTIPLE]. * * @param position The item whose checked state is to be checked * @param value The new checked state for the item */ - public void setItemChecked(int position, boolean value) { + fun setItemChecked(position: Int, value: Boolean) { if (mChoiceMode == CHOICE_MODE_NONE) { - return; + return } // Check is intoCheckMode - if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) { - throw new IllegalStateException("Call intoCheckMode first"); - } + check(!(mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !isInCustomChoice)) { "Call intoCheckMode first" } // Start selection mode if needed. We don't need to if we're unchecking something. if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) { - if (mMultiChoiceModeCallback == null || - !mMultiChoiceModeCallback.hasWrappedCallback()) { - throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " + + check( + !(mMultiChoiceModeCallback == null || + !mMultiChoiceModeCallback!!.hasWrappedCallback()) + ) { + "EasyRecyclerView: attempted to start selection mode " + "for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " + - "supplied. Call setMultiChoiceModeListener to set a callback."); + "supplied. Call setMultiChoiceModeListener to set a callback." } - mChoiceActionMode = startActionMode(mMultiChoiceModeCallback); + mChoiceActionMode = startActionMode(mMultiChoiceModeCallback) } - if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL || mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - boolean oldValue = mCheckStates.get(position); - mCheckStates.put(position, value); - if (mCheckedIdStates != null && mAdapter.hasStableIds()) { + val oldValue = mCheckStates!![position] + mCheckStates!!.put(position, value) + if (mCheckedIdStates != null && mAdapter!!.hasStableIds()) { if (value) { - mCheckedIdStates.put(mAdapter.getItemId(position), position); + mCheckedIdStates!!.put(mAdapter!!.getItemId(position), position) } else { - mCheckedIdStates.remove(mAdapter.getItemId(position)); + mCheckedIdStates!!.remove(mAdapter!!.getItemId(position)) } } if (oldValue != value) { if (value) { - mCheckedItemCount++; + checkedItemCount++ } else { - mCheckedItemCount--; + checkedItemCount-- } } if (mChoiceActionMode != null) { - final long id = mAdapter.getItemId(position); - mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, - position, id, value); + val id = mAdapter!!.getItemId(position) + mMultiChoiceModeCallback!!.onItemCheckedStateChanged( + mChoiceActionMode!!, + position, id, value + ) } if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { - final long id = mAdapter.getItemId(position); - mCustomChoiceListener.onItemCheckedStateChanged(this, position, id, value); + val id = mAdapter!!.getItemId(position) + mCustomChoiceListener!!.onItemCheckedStateChanged(this, position, id, value) } } else { - boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds(); + val updateIds = mCheckedIdStates != null && mAdapter!!.hasStableIds() // Clear all values if we're checking something, or unchecking the currently // selected item if (value || isItemChecked(position)) { - mCheckStates.clear(); + mCheckStates!!.clear() if (updateIds) { - mCheckedIdStates.clear(); + mCheckedIdStates!!.clear() } } // this may end up selecting the value we just cleared but this way // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on if (value) { - mCheckStates.put(position, true); + mCheckStates!!.put(position, true) if (updateIds) { - mCheckedIdStates.put(mAdapter.getItemId(position), position); + mCheckedIdStates!!.put(mAdapter!!.getItemId(position), position) } - mCheckedItemCount = 1; - } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { - mCheckedItemCount = 0; + checkedItemCount = 1 + } else if (mCheckStates!!.size() == 0 || !mCheckStates!!.valueAt(0)) { + checkedItemCount = 0 } } - - updateOnScreenCheckedViews(); + updateOnScreenCheckedViews() } /** * Defines the choice behavior for the List. By default, Lists do not have any choice behavior - * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the + * ([.CHOICE_MODE_NONE]). By setting the choiceMode to [.CHOICE_MODE_SINGLE], the * List allows up to one item to be in a chosen state. By setting the choiceMode to - * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen. + * [.CHOICE_MODE_MULTIPLE], the list allows any number of items to be chosen. * - * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or - * {@link #CHOICE_MODE_MULTIPLE} + * @param choiceMode One of [.CHOICE_MODE_NONE], [.CHOICE_MODE_SINGLE], or + * [.CHOICE_MODE_MULTIPLE] */ - public void setChoiceMode(int choiceMode) { - mChoiceMode = choiceMode; + fun setChoiceMode(choiceMode: Int) { + mChoiceMode = choiceMode if (mChoiceActionMode != null) { - mChoiceActionMode.finish(); - mChoiceActionMode = null; + mChoiceActionMode!!.finish() + mChoiceActionMode = null } if (mChoiceMode != CHOICE_MODE_NONE) { if (mCheckStates == null) { - mCheckStates = new SparseBooleanArray(0); + mCheckStates = SparseBooleanArray(0) } - if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { - mCheckedIdStates = new LongSparseArray<>(0); + if (mCheckedIdStates == null && mAdapter != null && mAdapter!!.hasStableIds()) { + mCheckedIdStates = LongSparseArray(0) } // Modal multi-choice mode only has choices when the mode is active. Clear them. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { - clearChoices(); - setLongClickable(true); + clearChoices() + isLongClickable = true } else if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) { if (mTempCheckStates == null) { - mTempCheckStates = new SparseBooleanArray(0); + mTempCheckStates = SparseBooleanArray(0) } - clearChoices(); + clearChoices() } } } - public void setCustomCheckedListener(CustomChoiceListener listener) { - mCustomChoiceListener = listener; + fun setCustomCheckedListener(listener: CustomChoiceListener?) { + mCustomChoiceListener = listener } /** @@ -437,238 +386,254 @@ public class EasyRecyclerView extends RecyclerView { * on all visible item views. This should only be called when a valid * choice mode is active. */ - private void updateOnScreenCheckedViews() { - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final int position = getChildAdapterPosition(child); - setViewChecked(child, mCheckStates.get(position)); + private fun updateOnScreenCheckedViews() { + val count = childCount + for (i in 0 until count) { + val child = getChildAt(i) + val position = getChildAdapterPosition(child) + setViewChecked(child, mCheckStates!![position]) } } - private void init(Context context) { - float scale = context.getResources().getDisplayMetrics().density; + private fun init(context: Context) { + val scale = context.resources.displayMetrics.density // g (m/s^2) // inch/meter // A context-specific coefficient adjusted to physical values. // look and feel tuning - pageScrollThreshold = scale * 80; + pageScrollThreshold = scale * 80 } - @Override - public Parcelable onSaveInstanceState() { - final SavedState ss = new SavedState(super.onSaveInstanceState()); - - ss.choiceMode = mChoiceMode; - ss.customChoice = mCustomChoice; - ss.checkedItemCount = mCheckedItemCount; - ss.checkState = mCheckStates; - ss.checkIdState = mCheckedIdStates; - - return ss; + public override fun onSaveInstanceState(): Parcelable? { + val ss = SavedState(super.onSaveInstanceState()) + ss.choiceMode = mChoiceMode + ss.customChoice = isInCustomChoice + ss.checkedItemCount = checkedItemCount + ss.checkState = mCheckStates + ss.checkIdState = mCheckedIdStates + return ss } - @Override - public void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - - setChoiceMode(ss.choiceMode); - mCustomChoice = ss.customChoice; - mCheckedItemCount = ss.checkedItemCount; + public override fun onRestoreInstanceState(state: Parcelable) { + val ss = state as SavedState + super.onRestoreInstanceState(ss.superState) + setChoiceMode(ss.choiceMode) + isInCustomChoice = ss.customChoice + checkedItemCount = ss.checkedItemCount if (ss.checkState != null) { - mCheckStates = ss.checkState; + mCheckStates = ss.checkState } if (ss.checkIdState != null) { - mCheckedIdStates = ss.checkIdState; + mCheckedIdStates = ss.checkIdState } - - if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mCheckedItemCount > 0) { - mChoiceActionMode = startActionMode(mMultiChoiceModeCallback); + if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && checkedItemCount > 0) { + mChoiceActionMode = startActionMode(mMultiChoiceModeCallback) } - updateOnScreenCheckedViews(); + updateOnScreenCheckedViews() } /** - * A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. - * It acts as the {@link android.view.ActionMode.Callback} for the selection mode and also receives - * {@link #onItemCheckedStateChanged(android.view.ActionMode, int, long, boolean)} events when the user + * A MultiChoiceModeListener receives events for [android.widget.AbsListView.CHOICE_MODE_MULTIPLE_MODAL]. + * It acts as the [android.view.ActionMode.Callback] for the selection mode and also receives + * [.onItemCheckedStateChanged] events when the user * selects and deselects list items. */ - public interface MultiChoiceModeListener extends ActionMode.Callback { + interface MultiChoiceModeListener : ActionMode.Callback { /** * Called when an item is checked or unchecked during selection mode. * - * @param mode The {@link android.view.ActionMode} providing the selection mode + * @param mode The [android.view.ActionMode] providing the selection mode * @param position Adapter position of the item that was checked or unchecked * @param id Adapter ID of the item that was checked or unchecked - * @param checked true if the item is now checked, false - * if the item is now unchecked. + * @param checked `true` if the item is now checked, `false` + * if the item is now unchecked. */ - void onItemCheckedStateChanged(ActionMode mode, - int position, long id, boolean checked); + fun onItemCheckedStateChanged( + mode: ActionMode, + position: Int, id: Long, checked: Boolean + ) } /** * Custom checked */ - public interface CustomChoiceListener { - - void onIntoCustomChoice(EasyRecyclerView view); - - void onOutOfCustomChoice(EasyRecyclerView view); - - void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked); + interface CustomChoiceListener { + fun onIntoCustomChoice(view: EasyRecyclerView) + fun onOutOfCustomChoice(view: EasyRecyclerView) + fun onItemCheckedStateChanged( + view: EasyRecyclerView, + position: Int, + id: Long, + checked: Boolean + ) } /** * This saved state class is a Parcelable and should not extend - * {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState} + * [android.view.View.BaseSavedState] nor [android.view.AbsSavedState] * because its super class AbsSavedState's constructor * currently passes null * as a class loader to read its superstate from Parcelable. - * This causes {@link android.os.BadParcelableException} when restoring saved states. - *

+ * This causes [android.os.BadParcelableException] when restoring saved states. + * + * * The super class "RecyclerView" is a part of the support library, * and restoring its saved state requires the class loader that loaded the RecyclerView. * It seems that the class loader is not required when restoring from RecyclerView itself, * but it is required when restoring from RecyclerView's subclasses. */ - static class SavedState implements Parcelable { + internal open class SavedState : Parcelable { + var choiceMode = 0 + var customChoice = false + var checkedItemCount = 0 + var checkState: SparseBooleanArray? = null + var checkIdState: LongSparseArray? = null - public static final SavedState EMPTY_STATE = new SavedState() { - }; - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - int choiceMode; - boolean customChoice; - int checkedItemCount; - SparseBooleanArray checkState; - LongSparseArray checkIdState; // This keeps the parent(RecyclerView)'s state - Parcelable mSuperState; + var superState: Parcelable? - SavedState() { - mSuperState = null; + constructor() { + superState = null } /** - * Constructor called from {@link #onSaveInstanceState()} + * Constructor called from [.onSaveInstanceState] */ - SavedState(Parcelable superState) { - mSuperState = superState != EMPTY_STATE ? superState : null; + constructor(superState: Parcelable?) { + this.superState = if (superState !== EMPTY_STATE) superState else null } /** - * Constructor called from {@link #CREATOR} + * Constructor called from [.CREATOR] */ - private SavedState(Parcel in) { + private constructor(`in`: Parcel) { // Parcel 'in' has its parent(RecyclerView)'s saved state. // To restore it, class loader that loaded RecyclerView is required. - Parcelable superState = in.readParcelable(RecyclerView.class.getClassLoader()); - mSuperState = superState != null ? superState : EMPTY_STATE; - - choiceMode = in.readInt(); - customChoice = NumberUtils.int2boolean(in.readInt()); - checkedItemCount = in.readInt(); - checkState = in.readSparseBooleanArray(); - final int N = in.readInt(); - if (N > 0) { - checkIdState = new LongSparseArray<>(); - for (int i = 0; i < N; i++) { - final long key = in.readLong(); - final int value = in.readInt(); - checkIdState.put(key, value); + val superState = + readParcelable(`in`, RecyclerView::class.java.classLoader, Parcelable::class.java) + this.superState = superState ?: EMPTY_STATE + choiceMode = `in`.readInt() + customChoice = int2boolean(`in`.readInt()) + checkedItemCount = `in`.readInt() + checkState = `in`.readSparseBooleanArray() + val n = `in`.readInt() + if (n > 0) { + checkIdState = LongSparseArray() + for (i in 0 until n) { + val key = `in`.readLong() + val value = `in`.readInt() + checkIdState!!.put(key, value) } } } - @Override - public int describeContents() { - return 0; + override fun describeContents(): Int { + return 0 } - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(mSuperState, flags); - - out.writeInt(choiceMode); - out.writeInt(NumberUtils.boolean2int(customChoice)); - out.writeInt(checkedItemCount); - out.writeSparseBooleanArray(checkState); - final int N = checkIdState != null ? checkIdState.size() : 0; - out.writeInt(N); - for (int i = 0; i < N; i++) { - out.writeLong(checkIdState.keyAt(i)); - out.writeInt(checkIdState.valueAt(i)); + override fun writeToParcel(out: Parcel, flags: Int) { + out.writeParcelable(superState, flags) + out.writeInt(choiceMode) + out.writeInt(boolean2int(customChoice)) + out.writeInt(checkedItemCount) + out.writeSparseBooleanArray(checkState) + val n = if (checkIdState != null) checkIdState!!.size() else 0 + out.writeInt(n) + for (i in 0 until n) { + out.writeLong(checkIdState!!.keyAt(i)) + out.writeInt(checkIdState!!.valueAt(i)) } } - public Parcelable getSuperState() { - return mSuperState; + companion object CREATOR : Parcelable.Creator { + private val EMPTY_STATE = SavedState() + + override fun createFromParcel(parcel: Parcel): SavedState { + return SavedState(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } } } - class MultiChoiceModeWrapper implements MultiChoiceModeListener { - private MultiChoiceModeListener mWrapped; - - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean hasWrappedCallback() { - return mWrapped != null; + inner class MultiChoiceModeWrapper : MultiChoiceModeListener { + private val mWrapped: MultiChoiceModeListener? = null + fun hasWrappedCallback(): Boolean { + return mWrapped != null } - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - if (mWrapped.onCreateActionMode(mode, menu)) { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + if (mWrapped!!.onCreateActionMode(mode, menu)) { // Initialize checked graphic state? - setLongClickable(false); - return true; + isLongClickable = false + return true } - return false; + return false } - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return mWrapped.onPrepareActionMode(mode, menu); + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + return mWrapped!!.onPrepareActionMode(mode, menu) } - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return mWrapped.onActionItemClicked(mode, item); + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return mWrapped!!.onActionItemClicked(mode, item) } - @Override - public void onDestroyActionMode(ActionMode mode) { - mWrapped.onDestroyActionMode(mode); - mChoiceActionMode = null; + override fun onDestroyActionMode(mode: ActionMode) { + mWrapped!!.onDestroyActionMode(mode) + mChoiceActionMode = null // Ending selection mode means deselecting everything. - clearChoices(); - - requestLayout(); - - setLongClickable(true); + clearChoices() + requestLayout() + isLongClickable = true } - @Override - public void onItemCheckedStateChanged(ActionMode mode, - int position, long id, boolean checked) { - mWrapped.onItemCheckedStateChanged(mode, position, id, checked); + override fun onItemCheckedStateChanged( + mode: ActionMode, + position: Int, id: Long, checked: Boolean + ) { + mWrapped!!.onItemCheckedStateChanged(mode, position, id, checked) // If there are no items selected we no longer need the selection mode. - if (getCheckedItemCount() == 0) { - mode.finish(); + if (checkedItemCount == 0) { + mode.finish() + } + } + } + + companion object { + /** + * Normal list that does not indicate choices + */ + const val CHOICE_MODE_NONE = 0 + + /** + * The list allows up to one choice + */ + const val CHOICE_MODE_SINGLE = 1 + + /** + * The list allows multiple choices + */ + const val CHOICE_MODE_MULTIPLE = 2 + + /** + * The list allows multiple choices in a modal selection mode + */ + const val CHOICE_MODE_MULTIPLE_MODAL = 3 + + /** + * The list allows multiple choices in custom action + */ + const val CHOICE_MODE_MULTIPLE_CUSTOM = 4 + fun setViewChecked(view: View, checked: Boolean) { + if (view is Checkable) { + (view as Checkable).isChecked = checked + } else { + view.isActivated = checked } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index a8480dfa9..bcaaaa84c 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -439,16 +439,12 @@ class DownloadsScene : NewLabelDialogHelper(builder, dialog) return@setOnMenuItemClickListener true } else if (id == R.id.action_default_download_label) { - val list = DownloadManager.labelList - val items = arrayOfNulls(list.size + 2) - items[0] = getString(R.string.let_me_select) - items[1] = getString(R.string.default_download_label_name) - var i = 0 - val n = list.size - while (i < n) { - items[i + 2] = list[i].label - i++ - } + val list = DownloadManager.labelList.mapNotNull { it.label }.toTypedArray() + val items = arrayOf( + getString(R.string.let_me_select), + getString(R.string.default_download_label_name), + *list + ) BaseDialogBuilder(requireContext()) .setTitle(R.string.default_download_label) .setItems(items) { _: DialogInterface?, which: Int -> @@ -539,7 +535,7 @@ class DownloadsScene : if (collectDownloadInfo) { downloadInfoList = LinkedList() } - val stateArray = binding.recyclerView.checkedItemPositions + val stateArray = binding.recyclerView.checkedItemPositions!! for (i in 0 until stateArray.size()) { if (stateArray.valueAt(i)) { val info = list[stateArray.keyAt(i)] diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index d134a3288..af0c39f79 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -485,17 +485,14 @@ class FavoritesScene : return } mModifyGiList.clear() - val stateArray = binding.contentLayout.recyclerView.checkedItemPositions - var i = 0 - val n = stateArray.size() - while (i < n) { + val stateArray = binding.contentLayout.recyclerView.checkedItemPositions!! + for (i in 0 until stateArray.size()) { if (stateArray.valueAt(i)) { val gi = mHelper.getDataAtEx(stateArray.keyAt(i)) if (gi != null) { mModifyGiList.add(gi) } } - i++ } when (position) { 3 -> // Check all @@ -696,13 +693,7 @@ class FavoritesScene : } binding.contentLayout.recyclerView.outOfCustomChoiceMode() if (mUrlBuilder!!.favCat == FavListUrlBuilder.FAV_CAT_LOCAL) { // Delete local fav - val gidArray = LongArray(mModifyGiList.size) - var i = 0 - val n = mModifyGiList.size - while (i < n) { - gidArray[i] = mModifyGiList[i].gid - i++ - } + val gidArray = mModifyGiList.map { it.gid }.toLongArray() EhDB.removeLocalFavorites(gidArray) mModifyGiList.clear() mHelper.refresh() @@ -737,13 +728,7 @@ class FavoritesScene : } binding.contentLayout.recyclerView.outOfCustomChoiceMode() if (srcCat == FavListUrlBuilder.FAV_CAT_LOCAL) { // Move from local to cloud - val gidArray = LongArray(mModifyGiList.size) - var i = 0 - val n = mModifyGiList.size - while (i < n) { - gidArray[i] = mModifyGiList[i].gid - i++ - } + val gidArray = mModifyGiList.map { it.gid }.toLongArray() EhDB.removeLocalFavorites(gidArray) mEnableModify = true mModifyFavCat = dstCat @@ -811,22 +796,13 @@ class FavoritesScene : if (mEnableModify) { mEnableModify = false val local = mUrlBuilder!!.favCat == FavListUrlBuilder.FAV_CAT_LOCAL - val gidArray = LongArray(mModifyGiList.size) if (mModifyAdd) { - val tokenArray = arrayOfNulls(mModifyGiList.size) - var i = 0 - val n = mModifyGiList.size - while (i < n) { - val gi = mModifyGiList[i] - gidArray[i] = gi.gid - tokenArray[i] = gi.token - i++ - } + val gidTokenArray = mModifyGiList.map { Pair(it.gid, it.token!!) }.toTypedArray() val modifyGiListBackup: List = ArrayList(mModifyGiList) mModifyGiList.clear() lifecycleScope.launchIO { runSuspendCatching { - EhEngine.addFavoritesRange(gidArray, tokenArray, mModifyFavCat) + EhEngine.addFavoritesRange(gidTokenArray, mModifyFavCat) }.onSuccess { withUIContext { onGetFavoritesLocal(mUrlBuilder?.keyword, taskId) @@ -841,12 +817,7 @@ class FavoritesScene : } } } else { - var i = 0 - val n = mModifyGiList.size - while (i < n) { - gidArray[i] = mModifyGiList[i].gid - i++ - } + val gidArray = mModifyGiList.map { it.gid }.toLongArray() mModifyGiList.clear() val url: String = if (local) { // Local fav is shown now, but operation need be done for cloud fav