From e5ce8fd0a69737663c990d86c3d03085656ff6af Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 2 Dec 2015 00:31:18 +0800 Subject: [PATCH] Refine Tasker features --- src/main/res/drawable-anydpi-v21/ic_check.xml | 9 - src/main/res/drawable-hdpi/ic_check.png | Bin 181 -> 0 bytes src/main/res/drawable-mdpi/ic_check.png | Bin 137 -> 0 bytes src/main/res/drawable-xhdpi/ic_check.png | Bin 199 -> 0 bytes src/main/res/drawable-xxhdpi/ic_check.png | Bin 276 -> 0 bytes src/main/res/drawable-xxxhdpi/ic_check.png | Bin 308 -> 0 bytes src/main/res/layout/layout_tasker.xml | 27 ++- .../layout/layout_tasker_toggle_service.xml | 13 -- src/main/res/menu/tasker_menu.xml | 10 - src/main/res/values-zh/strings.xml | 7 +- src/main/res/values/arrays.xml | 10 - src/main/res/values/strings.xml | 13 +- .../shadowsocks/ProfileManagerActivity.scala | 2 +- .../github/shadowsocks/TaskerActivity.scala | 213 +++++------------- .../github/shadowsocks/TaskerReceiver.scala | 22 +- .../shadowsocks/helper/TaskerSettings.scala | 77 ++----- 16 files changed, 109 insertions(+), 294 deletions(-) delete mode 100644 src/main/res/drawable-anydpi-v21/ic_check.xml delete mode 100644 src/main/res/drawable-hdpi/ic_check.png delete mode 100644 src/main/res/drawable-mdpi/ic_check.png delete mode 100644 src/main/res/drawable-xhdpi/ic_check.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_check.png delete mode 100644 src/main/res/drawable-xxxhdpi/ic_check.png delete mode 100644 src/main/res/layout/layout_tasker_toggle_service.xml delete mode 100644 src/main/res/menu/tasker_menu.xml diff --git a/src/main/res/drawable-anydpi-v21/ic_check.xml b/src/main/res/drawable-anydpi-v21/ic_check.xml deleted file mode 100644 index 6541ee3e..00000000 --- a/src/main/res/drawable-anydpi-v21/ic_check.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/main/res/drawable-hdpi/ic_check.png b/src/main/res/drawable-hdpi/ic_check.png deleted file mode 100644 index 729f290104788ce25a026cd1842e625ed29469d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8ji-xah{y4_S9bF@81T3TmS5N? z#C(9Q$LV3`iyt|zzNMK?OD+gTJ2maGvJ86jqI1fWJrY@L@@dRh+8HxyC3MfUOIWFC zSzj(@_&m4jkH)bAn}wUwn%lHbyQcPAEcKb)b;T<=reaCi=lz``&(3vDxn&&?DzJK z|MUJvlc)bXuNjm~`JeuC|Cf3T&Sn4kS-t-Kly_`O{%78>)n(FyvK7-bg!XV3_cwNy;D|IOb43H;OXk;vd$@?2>^BlGsOS^ diff --git a/src/main/res/drawable-xhdpi/ic_check.png b/src/main/res/drawable-xhdpi/ic_check.png deleted file mode 100644 index 3b2b65d26291575f2741d223cdf80facb436dc20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DK2I0Nkcif|mv{3vD2TKsT6rJ; zxO0YV_Rhw~KO`R95NK`?{Vrkn{Yyi*jo}i373|mq*}I4NcaPr1vPBqUj4{So^{}zU z+2V@M76*V4(wk$0gq_FHCw*~rLF6PjJvrp8;xy!tvzjxILyidcxS;vzge%E8;0~bA zCza(K@t8(4No6@FJiXx|O)AO}!Fe{GAi-fQe6!HylPYi`B&W#9B~|3)kkD4;1WDtE zgcdi2M1XboCryi6C~+>FcwG}oCT5=CqmNX1W4MP8OZ|2BU#~0BugB|7-NjF ae|iA|xq+-YC!0F}0000 diff --git a/src/main/res/drawable-xxxhdpi/ic_check.png b/src/main/res/drawable-xxxhdpi/ic_check.png deleted file mode 100644 index d670618c7e96225f7756cb4c2743e7ebbf688cf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgetWt&hE&{od;P3vlYxj!B3Hnh z1714EonD+``t$ZU)3IJ|2aboi6~%ilJM7!-={Kv*@R&q$518Ox)_m^XpXp1^&DOu% zB6r^~@kqJ%j0e`LXFkL-JviL|Ir_(khlktwLa#r83bn@|VwIw^gKjzH(>hUq>tlAsh9~)aj{(R+p z`Qx=v&?j!r?}<)Pb+Jxa|B9Td>MRd7#ru4CATBh=u6rtH - + - + + + + diff --git a/src/main/res/layout/layout_tasker_toggle_service.xml b/src/main/res/layout/layout_tasker_toggle_service.xml deleted file mode 100644 index e182bc14..00000000 --- a/src/main/res/layout/layout_tasker_toggle_service.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/src/main/res/menu/tasker_menu.xml b/src/main/res/menu/tasker_menu.xml deleted file mode 100644 index 670f5ce0..00000000 --- a/src/main/res/menu/tasker_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 8e7e5697..723562c1 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -97,7 +97,10 @@ 已删除 撤销 - 切换服务状态 - 保存 + 启动服务 + 连接到当前服务器 + 连接到 %s + 切换到 %s + 使用当前配置 diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index f5b7f3ac..3f3fa9ef 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -226,14 +226,4 @@ 204.0.0.0/6 208.0.0.0/4 - - - Toggle service - Switch profile - - - - toggle_service - switch_profile - diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7596b654..2893b509 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -103,12 +103,9 @@ Undo - Toggle service state - Save - Turn service %s - No action was selected - No profile was selected - Switch profile to %s - on - off + Start the service + Connect to the current server + Connect to %s + Switch to %s + Use the current profile diff --git a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala index 77aa09ce..e5c3df49 100644 --- a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala @@ -93,7 +93,7 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] { private val recycleBin = new ArrayBuffer[(Int, Profile)] private var profiles = new ArrayBuffer[Profile] - profiles ++= ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List[Profile]()) + profiles ++= ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile]) def getItemCount = profiles.length diff --git a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala index 3919659d..8f0c40f5 100644 --- a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala @@ -39,25 +39,63 @@ package com.github.shadowsocks import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.support.v4.app.{Fragment, ListFragment} +import android.content.res.Resources +import android.os.{Build, Bundle} import android.support.v7.app.AppCompatActivity -import android.support.v7.widget.Toolbar -import android.util.Log +import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar} import android.view.{LayoutInflater, View, ViewGroup} -import android.widget.AdapterView.OnItemClickListener -import android.widget._ +import android.widget.{Switch, CheckedTextView} import com.github.shadowsocks.database.Profile import com.github.shadowsocks.helper.TaskerSettings -import com.twofortyfouram.locale.api.{Intent => ApiIntent} /** * @author CzBiX */ class TaskerActivity extends AppCompatActivity { - var taskerOption: TaskerSettings = _ - var actionSelected: Boolean = false + private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { + { + val typedArray = obtainStyledAttributes(Array(android.R.attr.selectableItemBackground)) + view.setBackgroundResource(typedArray.getResourceId(0, 0)) + typedArray.recycle + } + private var item: Profile = _ + private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView] + itemView.setOnClickListener(this) + + def bindDefault { + item = null + text.setText(R.string.profile_default) + text.setChecked(taskerOption.profileId < 0) + } + def bind(item: Profile) { + this.item = item + text.setText(item.name) + text.setChecked(taskerOption.profileId == item.id) + } + + def onClick(v: View) { + taskerOption.switchOn = switch.isChecked + taskerOption.profileId = if (item == null) -1 else item.id + setResult(Activity.RESULT_OK, taskerOption.toIntent(TaskerActivity.this)) + finish + } + } + + private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] { + private val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile]) + def getItemCount = 1 + profiles.length + def onBindViewHolder(vh: ProfileViewHolder, i: Int) = i match { + case 0 => vh.bindDefault + case _ => vh.bind(profiles(i - 1)) + } + private val name = "select_dialog_singlechoice_" + (if (Build.VERSION.SDK_INT >= 21) "material" else "holo") + def onCreateViewHolder(vg: ViewGroup, i: Int) = new ProfileViewHolder(LayoutInflater.from(vg.getContext) + .inflate(Resources.getSystem.getIdentifier(name, "layout", "android"), vg, false)) + } + + private var taskerOption: TaskerSettings = _ + private var switch: Switch = _ + private val profilesAdapter = new ProfilesAdapter override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) @@ -67,155 +105,14 @@ class TaskerActivity extends AppCompatActivity { toolbar.setTitle(R.string.app_name) toolbar.setNavigationIcon(R.drawable.ic_close) toolbar.setNavigationOnClickListener(_ => finish()) - toolbar.inflateMenu(R.menu.tasker_menu) - toolbar.setOnMenuItemClickListener(_.getItemId match { - case R.id.save => - saveResult() - true - case _ => false - }) - loadSettings() - loadFragment() - } - - private def loadSettings() { - val intent: Intent = getIntent - if (intent.getAction != ApiIntent.ACTION_EDIT_SETTING) { - Log.w(Shadowsocks.TAG, "unknown tasker action") - finish() - return - } - - taskerOption = TaskerSettings.fromIntent(intent) - } - - private def loadFragment() { - actionSelected = !taskerOption.isEmpty - val fragment: Fragment = taskerOption.action match { - case TaskerSettings.ACTION_TOGGLE_SERVICE => - new ToggleServiceFragment - case TaskerSettings.ACTION_SWITCH_PROFILE => - new ProfileChoiceFragment - case _ => - val fragment: ActionChoiceFragment = new ActionChoiceFragment - fragment.onItemClickedListener = (_, _, _, id) => { - taskerOption.action = getResources.getStringArray(R.array.tasker_action_value)(id.toInt) - loadFragment() - actionSelected = true - } - fragment - } - - getSupportFragmentManager.beginTransaction() - .replace(R.id.fragment, fragment) - .commit() - } - - private def saveResult() { - if (actionSelected) { - val fragment: OptionFragment = getSupportFragmentManager.findFragmentById(R.id.fragment).asInstanceOf[OptionFragment] - if (fragment.saveResult()) { - setResult(Activity.RESULT_OK, taskerOption.toIntent(this)) - finish() - } - } else { - Toast.makeText(this, R.string.no_action_selected, Toast.LENGTH_SHORT).show() - } - } - - private class ActionChoiceFragment extends ListFragment { - var onItemClickedListener: OnItemClickListener = _ - - override def onActivityCreated(savedInstanceState: Bundle) { - super.onActivityCreated(savedInstanceState) - - val adapter = ArrayAdapter.createFromResource(getActivity, R.array.tasker_action_name, android.R.layout.simple_selectable_list_item) - - setListAdapter(adapter) - } - - override def onListItemClick(l: ListView, v: View, position: Int, id: Long) { - if (onItemClickedListener != null) { - onItemClickedListener.onItemClick(l, v, position, id) - } - } - } - - private class ToggleServiceFragment extends Fragment with OptionFragment { - var switch: Switch = _ - - override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View = { - val view: View = inflater.inflate(R.layout.layout_tasker_toggle_service, container, false) - - switch = view.findViewById(R.id.service_switch).asInstanceOf[Switch] - view - } - - override def onActivityCreated(savedInstanceState: Bundle) { - super.onActivityCreated(savedInstanceState) - - if (!taskerOption.isEmpty) { - switch.setChecked(taskerOption.isStart) - } - } - - override def saveResult() = { - taskerOption.action = TaskerSettings.ACTION_TOGGLE_SERVICE - taskerOption.isStart = switch.isChecked - true - } - } - - private class ProfileChoiceFragment extends ListFragment with OptionFragment { - override def onActivityCreated(savedInstanceState: Bundle) { - super.onActivityCreated(savedInstanceState) - - import scala.collection.JavaConverters._ - val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty) - val adapter = new ArrayAdapter[Profile](getActivity, 0, android.R.id.text1, profiles.asJava) { - override def getView(position: Int, convertView: View, parent: ViewGroup): View = { - val view: TextView = if (convertView == null) { - val inflater = LayoutInflater.from(parent.getContext) - inflater.inflate(android.R.layout.simple_list_item_single_choice, parent, false).asInstanceOf[TextView] - } else { - convertView.asInstanceOf[TextView] - } - - view.setText(getItem(position).name) - view - } - } - - setListAdapter(adapter) - - val listView: ListView = getListView - listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE) - - if (!taskerOption.isEmpty) { - val index = profiles.indexWhere(_.id == taskerOption.profileId) - if (index > -1) { - listView.setItemChecked(index, true) - } - } - } - - override def saveResult() = { - taskerOption.action = TaskerSettings.ACTION_SWITCH_PROFILE - val listView = getListView - val item = listView.getItemAtPosition(listView.getCheckedItemPosition) - if (item == null) { - Toast.makeText(getActivity, R.string.no_profile_selected, Toast.LENGTH_SHORT).show() - false - } else { - taskerOption.profileId = item.asInstanceOf[Profile].id - true - } - } - } - - trait OptionFragment extends Fragment { - def saveResult(): Boolean + taskerOption = TaskerSettings.fromIntent(getIntent) + switch = findViewById(R.id.serviceSwitch).asInstanceOf[Switch] + switch.setChecked(taskerOption.switchOn) + val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView] + profilesList.setLayoutManager(new LinearLayoutManager(this)) + profilesList.setItemAnimator(new DefaultItemAnimator) + profilesList.setAdapter(profilesAdapter) } } diff --git a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala index 06862287..a99c0284 100644 --- a/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/TaskerReceiver.scala @@ -39,7 +39,6 @@ package com.github.shadowsocks import android.content.{BroadcastReceiver, Context, Intent} -import android.util.Log import com.github.shadowsocks.helper.TaskerSettings import com.github.shadowsocks.utils.Utils @@ -47,20 +46,15 @@ import com.github.shadowsocks.utils.Utils * @author CzBiX */ class TaskerReceiver extends BroadcastReceiver { - override def onReceive(context: Context, intent: Intent): Unit = { - val settings: TaskerSettings = TaskerSettings.fromIntent(intent) - - settings.action match { - case TaskerSettings.ACTION_TOGGLE_SERVICE => - if (settings.isStart) { - Utils.startSsService(context) - } else { - Utils.stopSsService(context) - } - case TaskerSettings.ACTION_SWITCH_PROFILE => + override def onReceive(context: Context, intent: Intent) { + val settings = TaskerSettings.fromIntent(intent) + val switched = ShadowsocksApplication.profileManager.getProfile(settings.profileId) match { + case Some(p) => + Utils.stopSsService(context) ShadowsocksApplication.switchProfile(settings.profileId) - case _ => - Log.e(Shadowsocks.TAG, s"unknown tasker action: ${settings.action}") + true + case _ => false } + if (settings.switchOn) Utils.startSsService(context) else if (!switched) Utils.stopSsService(context) } } diff --git a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala b/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala index a9cbc1bd..8fadf78a 100644 --- a/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala +++ b/src/main/scala/com/github/shadowsocks/helper/TaskerSettings.scala @@ -40,78 +40,35 @@ package com.github.shadowsocks.helper import android.content.{Context, Intent} import android.os.Bundle -import android.util.Log -import com.github.shadowsocks.{R, Shadowsocks, ShadowsocksApplication} +import com.github.shadowsocks.{R, ShadowsocksApplication} import com.twofortyfouram.locale.api.{Intent => ApiIntent} object TaskerSettings { - val ACTION_UNKNOWN = "unknown" - val ACTION_TOGGLE_SERVICE = "toggle_service" - val ACTION_SWITCH_PROFILE = "switch_profile" + private val KEY_ACTION = "action" + private val KEY_SWITCH_ON = "switch_on" + private val KEY_PROFILE_ID = "profile_id" - def fromIntent(intent: Intent): TaskerSettings = { - val bundle: Bundle = if (intent.hasExtra(ApiIntent.EXTRA_BUNDLE)) - intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE) else Bundle.EMPTY + def fromIntent(intent: Intent) = new TaskerSettings(if (intent.hasExtra(ApiIntent.EXTRA_BUNDLE)) + intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE) else Bundle.EMPTY) - new TaskerSettings(bundle) - } - - def fromBundle(bundle: Bundle): TaskerSettings = { - new TaskerSettings(bundle) - } + def fromBundle(bundle: Bundle) = new TaskerSettings(bundle) } class TaskerSettings(bundle: Bundle) { import TaskerSettings._ - private val KEY_ACTION = "action" - private val KEY_IS_START = "is_start" - private val KEY_PROFILE_ID = "profile_id" + var switchOn = bundle.getBoolean(KEY_SWITCH_ON, true) + var profileId = bundle.getInt(KEY_PROFILE_ID, -1) - var action: String = bundle.getString(KEY_ACTION, ACTION_UNKNOWN) - - var isStart: Boolean = _ - var profileId: Int = _ - - action match { - case ACTION_TOGGLE_SERVICE => - isStart = bundle.getBoolean(KEY_IS_START, true) - case ACTION_SWITCH_PROFILE => - profileId = bundle.getInt(KEY_PROFILE_ID, -1) - assert(profileId != -1, "profile id was wrong") - case _ => - Log.w(Shadowsocks.TAG, s"unknown tasker action settings: $action") - } - - def isEmpty = action == ACTION_UNKNOWN - - def toIntent(context: Context): Intent = { + def toIntent(context: Context) = { val bundle = new Bundle() - - action match { - case ACTION_TOGGLE_SERVICE => - bundle.putString(KEY_ACTION, action) - bundle.putBoolean(KEY_IS_START, isStart) - case ACTION_SWITCH_PROFILE => - bundle.putString(KEY_ACTION, action) - bundle.putInt(KEY_PROFILE_ID, profileId) - } - - val desc: String = action match { - case ACTION_TOGGLE_SERVICE => - context.getString(R.string.turn_service_state, - context.getString(if (isStart) R.string.state_on else R.string.state_off)) - case ACTION_SWITCH_PROFILE => - val profileName = ShadowsocksApplication.profileManager.getProfile(profileId) match { - case Some(p) => p.name - case None => context.getString(R.string.removed) - } - context.getString(R.string.switch_profile_to, profileName) - } - - val intent: Intent = new Intent() - intent.putExtra(ApiIntent.EXTRA_STRING_BLURB, desc) - .putExtra(ApiIntent.EXTRA_BUNDLE, bundle) + if (!switchOn) bundle.putBoolean(KEY_SWITCH_ON, false) + if (profileId >= 0) bundle.putInt(KEY_PROFILE_ID, profileId) + new Intent().putExtra(ApiIntent.EXTRA_BUNDLE, bundle).putExtra(ApiIntent.EXTRA_STRING_BLURB, + ShadowsocksApplication.profileManager.getProfile(profileId) match { + case Some(p) => context.getString(if (switchOn) R.string.start_service else R.string.stop_service, p.name) + case None => context.getString(if (switchOn) R.string.start_service_default else R.string.stop) + }) } }