Commit 0030d8aa authored by Max Lv's avatar Max Lv

Fix animation and icons

parent 77d2def9
......@@ -20,19 +20,11 @@
package com.github.shadowsocks.subscription
import android.content.Context
import android.util.Log
import androidx.recyclerview.widget.SortedList
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.asIterable
import java.io.File
import java.io.IOException
import java.io.Reader
import java.net.URL
import java.net.URLConnection
class Subscription {
companion object {
......@@ -49,19 +41,22 @@ class Subscription {
}
private abstract class BaseSorter<T> : SortedList.Callback<T>() {
override fun onInserted(position: Int, count: Int) { }
override fun onInserted(position: Int, count: Int) {}
override fun areContentsTheSame(oldItem: T?, newItem: T?): Boolean = oldItem == newItem
override fun onMoved(fromPosition: Int, toPosition: Int) { }
override fun onChanged(position: Int, count: Int) { }
override fun onRemoved(position: Int, count: Int) { }
override fun onMoved(fromPosition: Int, toPosition: Int) {}
override fun onChanged(position: Int, count: Int) {}
override fun onRemoved(position: Int, count: Int) {}
override fun areItemsTheSame(item1: T?, item2: T?): Boolean = item1 == item2
override fun compare(o1: T?, o2: T?): Int =
if (o1 == null) if (o2 == null) 0 else 1 else if (o2 == null) -1 else compareNonNull(o1, o2)
abstract fun compareNonNull(o1: T, o2: T): Int
}
private open class DefaultSorter<T : Comparable<T>> : BaseSorter<T>() {
override fun compareNonNull(o1: T, o2: T): Int = o1.compareTo(o2)
}
private object URLSorter : BaseSorter<URL>() {
private val ordering = compareBy<URL>({ it.host }, { it.port }, { it.file }, { it.protocol })
override fun compareNonNull(o1: URL, o2: URL): Int = ordering.compare(o1, o2)
......
......@@ -70,6 +70,7 @@ dependencies {
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation 'me.zhanghai.android.fastscroll:library:1.1.0'
implementation 'xyz.belvi.mobilevision:barcodescanner:2.0.3'
implementation 'me.zhanghai.android.materialprogressbar:library:1.6.1'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$androidTestVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidEspressoVersion"
......
......@@ -21,62 +21,52 @@
package com.github.shadowsocks.subscription
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.*
import android.widget.*
import android.widget.AdapterView
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.shadowsocks.Core
import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R
import com.github.shadowsocks.ToolbarFragment
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.utils.asIterable
import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.widget.ListHolderListener
import com.github.shadowsocks.widget.MainListListener
import com.github.shadowsocks.widget.UndoSnackbarManager
import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.parcel.Parcelize
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.net.IDN
import me.zhanghai.android.materialprogressbar.MaterialProgressBar
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL
import java.util.*
import java.util.regex.PatternSyntaxException
import com.github.shadowsocks.subscription.Subscription
import com.github.shadowsocks.utils.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream
import java.io.IOError
import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
companion object {
private const val REQUEST_CODE_ADD = 1
private const val REQUEST_CODE_EDIT = 2
private const val SELECTED_URLS = "com.github.shadowsocks.acl.subscription.SELECTED_URLS" }
private const val SELECTED_URLS = "com.github.shadowsocks.acl.subscription.SELECTED_URLS"
}
@Parcelize
......@@ -113,19 +103,19 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
validate()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) = validate(value = s)
override fun onNothingSelected(parent: AdapterView<*>?) = check(false)
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) = validate()
private fun validate(value: Editable = editText.text) {
var message = ""
positive.isEnabled = try {
val url = URL(value.toString())
if ("http".equals(url.protocol, true)) message = getString(R.string.cleartext_http_warning)
true
} catch (e: MalformedURLException) {
positive.isEnabled = try {
val url = URL(value.toString())
if ("http".equals(url.protocol, true)) message = getString(R.string.cleartext_http_warning)
true
} catch (e: MalformedURLException) {
message = e.readableMessage
false
}
......@@ -167,6 +157,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
if (selectedItems.isNotEmpty()) onLongClick(v)
else SubDialogFragment().withArg(SubItem(item.toString())).show(this@SubscriptionFragment, REQUEST_CODE_EDIT)
}
override fun onLongClick(v: View?): Boolean {
if (!selectedItems.add(item)) selectedItems.remove(item) // toggle
itemView.isSelected = !itemView.isSelected
......@@ -181,8 +172,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
override fun onBindViewHolder(holder: SubViewHolder, i: Int) {
holder.bind(subscription.urls[i])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = SubViewHolder(LayoutInflater
.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false))
override fun getItemCount(): Int = subscription.urls.size()
private fun apply() {
......@@ -237,30 +230,38 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
private val selectedItems = HashSet<Any>()
private val adapter by lazy { SubscriptionAdapter() }
private lateinit var list: RecyclerView
private lateinit var progress: MaterialProgressBar
private var mode: ActionMode? = null
private lateinit var undoManager: UndoSnackbarManager<Any>
private var fetchJob: Job? = null
private fun fetchServerFromSubscriptions() {
// SubscriptionSyncer.schedule()
val activity = activity as MainActivity
if (fetchJob?.isActive != true) {
val activity = activity as MainActivity
progress.visibility = View.VISIBLE
val job = GlobalScope.launch {
val subscription = Subscription.instance
fetchJob = GlobalScope.launch {
val subscription = Subscription.instance
try {
for (url in subscription.urls.asIterable()) {
val connection = url.openConnection() as HttpURLConnection
ProfileManager.createProfilesFromJson(sequenceOf(connection.inputStream))
try {
for (url in subscription.urls.asIterable()) {
val connection = url.openConnection() as HttpURLConnection
ProfileManager.createProfilesFromJson(sequenceOf(connection.inputStream),replace = true)
}
} catch (e: Exception) {
e.printStackTrace()
activity.snackbar(e.readableMessage).show()
} finally {
progress.post {
progress.visibility = View.INVISIBLE
}
}
} catch (e: Exception) {
e.printStackTrace()
activity.snackbar(e.readableMessage).show()
}
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.layout_custom_rules, container, false)
inflater.inflate(R.layout.layout_subscriptions, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......@@ -278,13 +279,16 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
list.layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
list.itemAnimator = DefaultItemAnimator()
list.adapter = adapter
progress = view.findViewById(R.id.indeterminate_horizontal_progress)
FastScrollerBuilder(list).useMd2Style().build()
undoManager = UndoSnackbarManager(activity, adapter::undo)
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START or ItemTouchHelper.END) {
override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int =
if (isEnabled && selectedItems.isEmpty()) super.getSwipeDirs(recyclerView, viewHolder) else 0
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) =
adapter.remove(viewHolder.adapterPosition)
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder): Boolean = false
}).attachToRecyclerView(list)
......@@ -325,7 +329,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
when (resultCode) {
DialogInterface.BUTTON_POSITIVE -> {
if (editing) adapter.remove(ret.replacing.toURL())
adapter.add(ret.edited.toURL())?.also { list.post { list.scrollToPosition(it) } }
adapter.add(ret.edited.toURL()).also { list.post { list.scrollToPosition(it) } }
}
DialogInterface.BUTTON_NEUTRAL -> ret.replacing.toURL().let { item ->
adapter.remove(item)
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<include layout="@layout/toolbar_light_dark" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/indeterminate_horizontal_progress"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:indeterminate="true"
android:visibility="invisible"
app:mpb_progressStyle="horizontal"
app:mpb_showProgressBackground="false"
app:mpb_useIntrinsicPadding="false" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@android:layout/simple_list_item_1" />
</FrameLayout>
</LinearLayout>
......@@ -7,7 +7,7 @@
android:icon="@drawable/ic_action_description"/>
<item android:id="@+id/subscriptions"
android:title="@string/subscriptions"
android:icon="@drawable/ic_action_description"/>
android:icon="@drawable/ic_action_cloud_download"/>
<item android:id="@+id/customRules"
android:title="@string/custom_rules"
android:icon="@drawable/ic_action_assignment"/>
......
......@@ -8,7 +8,7 @@
app:showAsAction="always"/>
<item android:title="@string/update_subscription"
android:id="@+id/action_update_subscription"
android:icon="@drawable/ic_action_done"
android:icon="@drawable/ic_action_cloud_download"
android:alphabeticShortcut="r"
app:showAsAction="ifRoom"/>
</menu>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment