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 729f2901..00000000 Binary files a/src/main/res/drawable-hdpi/ic_check.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ic_check.png b/src/main/res/drawable-mdpi/ic_check.png deleted file mode 100644 index dfcb55d0..00000000 Binary files a/src/main/res/drawable-mdpi/ic_check.png and /dev/null differ 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 3b2b65d2..00000000 Binary files a/src/main/res/drawable-xhdpi/ic_check.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ic_check.png b/src/main/res/drawable-xxhdpi/ic_check.png deleted file mode 100644 index 2c2ad771..00000000 Binary files a/src/main/res/drawable-xxhdpi/ic_check.png and /dev/null differ 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 d670618c..00000000 Binary files a/src/main/res/drawable-xxxhdpi/ic_check.png and /dev/null differ diff --git a/src/main/res/layout/layout_tasker.xml b/src/main/res/layout/layout_tasker.xml index 516fc5d4..c33cedeb 100644 --- a/src/main/res/layout/layout_tasker.xml +++ b/src/main/res/layout/layout_tasker.xml @@ -1,12 +1,21 @@ - + - + + + + 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) + }) } }