diff --git a/src/main/res/drawable-hdpi/md__category_background.9.png b/src/main/res/drawable-hdpi/md__category_background.9.png new file mode 100644 index 00000000..43a20adc Binary files /dev/null and b/src/main/res/drawable-hdpi/md__category_background.9.png differ diff --git a/src/main/res/drawable-hdpi/md__list_focused_holo.9.png b/src/main/res/drawable-hdpi/md__list_focused_holo.9.png new file mode 100644 index 00000000..55527084 Binary files /dev/null and b/src/main/res/drawable-hdpi/md__list_focused_holo.9.png differ diff --git a/src/main/res/drawable-hdpi/md__list_longpressed_holo.9.png b/src/main/res/drawable-hdpi/md__list_longpressed_holo.9.png new file mode 100644 index 00000000..4ea7afa0 Binary files /dev/null and b/src/main/res/drawable-hdpi/md__list_longpressed_holo.9.png differ diff --git a/src/main/res/drawable-hdpi/md__list_pressed_holo_dark.9.png b/src/main/res/drawable-hdpi/md__list_pressed_holo_dark.9.png new file mode 100644 index 00000000..5654cd69 Binary files /dev/null and b/src/main/res/drawable-hdpi/md__list_pressed_holo_dark.9.png differ diff --git a/src/main/res/drawable-hdpi/md__list_selector_disabled_holo_dark.9.png b/src/main/res/drawable-hdpi/md__list_selector_disabled_holo_dark.9.png new file mode 100644 index 00000000..f6fd30dc Binary files /dev/null and b/src/main/res/drawable-hdpi/md__list_selector_disabled_holo_dark.9.png differ diff --git a/src/main/res/drawable-mdpi/md__category_background.9.png b/src/main/res/drawable-mdpi/md__category_background.9.png new file mode 100644 index 00000000..af0bc168 Binary files /dev/null and b/src/main/res/drawable-mdpi/md__category_background.9.png differ diff --git a/src/main/res/drawable-mdpi/md__list_focused_holo.9.png b/src/main/res/drawable-mdpi/md__list_focused_holo.9.png new file mode 100644 index 00000000..00f05d8c Binary files /dev/null and b/src/main/res/drawable-mdpi/md__list_focused_holo.9.png differ diff --git a/src/main/res/drawable-mdpi/md__list_longpressed_holo.9.png b/src/main/res/drawable-mdpi/md__list_longpressed_holo.9.png new file mode 100644 index 00000000..3bf8e036 Binary files /dev/null and b/src/main/res/drawable-mdpi/md__list_longpressed_holo.9.png differ diff --git a/src/main/res/drawable-mdpi/md__list_pressed_holo_dark.9.png b/src/main/res/drawable-mdpi/md__list_pressed_holo_dark.9.png new file mode 100644 index 00000000..6e77525d Binary files /dev/null and b/src/main/res/drawable-mdpi/md__list_pressed_holo_dark.9.png differ diff --git a/src/main/res/drawable-mdpi/md__list_selector_disabled_holo_dark.9.png b/src/main/res/drawable-mdpi/md__list_selector_disabled_holo_dark.9.png new file mode 100644 index 00000000..92da2f0d Binary files /dev/null and b/src/main/res/drawable-mdpi/md__list_selector_disabled_holo_dark.9.png differ diff --git a/src/main/res/drawable-xhdpi/md__category_background.9.png b/src/main/res/drawable-xhdpi/md__category_background.9.png new file mode 100644 index 00000000..942d72e6 Binary files /dev/null and b/src/main/res/drawable-xhdpi/md__category_background.9.png differ diff --git a/src/main/res/drawable-xhdpi/md__list_focused_holo.9.png b/src/main/res/drawable-xhdpi/md__list_focused_holo.9.png new file mode 100644 index 00000000..b545f8e5 Binary files /dev/null and b/src/main/res/drawable-xhdpi/md__list_focused_holo.9.png differ diff --git a/src/main/res/drawable-xhdpi/md__list_longpressed_holo.9.png b/src/main/res/drawable-xhdpi/md__list_longpressed_holo.9.png new file mode 100644 index 00000000..eda10e61 Binary files /dev/null and b/src/main/res/drawable-xhdpi/md__list_longpressed_holo.9.png differ diff --git a/src/main/res/drawable-xhdpi/md__list_pressed_holo_dark.9.png b/src/main/res/drawable-xhdpi/md__list_pressed_holo_dark.9.png new file mode 100644 index 00000000..e4b33935 Binary files /dev/null and b/src/main/res/drawable-xhdpi/md__list_pressed_holo_dark.9.png differ diff --git a/src/main/res/drawable-xhdpi/md__list_selector_disabled_holo_dark.9.png b/src/main/res/drawable-xhdpi/md__list_selector_disabled_holo_dark.9.png new file mode 100644 index 00000000..88726b69 Binary files /dev/null and b/src/main/res/drawable-xhdpi/md__list_selector_disabled_holo_dark.9.png differ diff --git a/src/main/res/drawable/md__list_selector_background_transition_holo_dark.xml b/src/main/res/drawable/md__list_selector_background_transition_holo_dark.xml new file mode 100644 index 00000000..1d43396d --- /dev/null +++ b/src/main/res/drawable/md__list_selector_background_transition_holo_dark.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/src/main/res/drawable/md__list_selector_holo_dark.xml b/src/main/res/drawable/md__list_selector_holo_dark.xml new file mode 100644 index 00000000..bd71aa8b --- /dev/null +++ b/src/main/res/drawable/md__list_selector_holo_dark.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/src/main/res/layout/menu_row_category.xml b/src/main/res/layout/menu_row_category.xml new file mode 100644 index 00000000..c89e5d96 --- /dev/null +++ b/src/main/res/layout/menu_row_category.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/main/res/layout/menu_row_item.xml b/src/main/res/layout/menu_row_item.xml new file mode 100644 index 00000000..af2c4b38 --- /dev/null +++ b/src/main/res/layout/menu_row_item.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index bc48ae9a..c9f62e57 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -22,5 +22,7 @@ #646464 #c8c8c8 #323232 + #00000000 + #FF555555 \ No newline at end of file diff --git a/src/main/res/values/ids.xml b/src/main/res/values/ids.xml new file mode 100644 index 00000000..925ac058 --- /dev/null +++ b/src/main/res/values/ids.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 93e381d0..a2272424 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -29,5 +29,36 @@ @style/Widget.Holo.Light.CompoundButton.Switch + + + + \ No newline at end of file diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 8760a48c..47d83b39 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -46,7 +46,7 @@ import android.os._ import android.preference.{Preference, PreferenceManager} import android.util.Log import android.view.{ViewGroup, Gravity, ViewParent, KeyEvent} -import android.widget.{LinearLayout, CompoundButton, RelativeLayout, TextView} +import android.widget._ import com.actionbarsherlock.view.Menu import com.actionbarsherlock.view.MenuItem import com.google.analytics.tracking.android.EasyTracker @@ -68,7 +68,11 @@ import scala.concurrent.ops._ import android.content.SharedPreferences.OnSharedPreferenceChangeListener import com.google.ads.{AdRequest, AdSize, AdView} import net.simonvt.menudrawer.MenuDrawer -import javax.swing.text.html.ListView + +import com.github.shadowsocks.database.{MenuAdapter, Item, Category} +import scala.collection.mutable.ListBuffer +import com.github.shadowsocks.database.Category +import com.github.shadowsocks.database.Item object Shadowsocks { @@ -222,6 +226,8 @@ class Shadowsocks lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE) lazy val receiver = new StateBroadcastReceiver lazy val drawer = MenuDrawer.attach(this) + lazy val listView = new ListView(this) + lazy val menuAdapter = new MenuAdapter(this, getMenuList) private val handler: Handler = new Handler { override def handleMessage(msg: Message) { @@ -399,11 +405,8 @@ class Shadowsocks setHeaderRes(R.xml.shadowsocks_headers) super.onCreate(savedInstanceState) - val menuView = new TextView(this) - menuView.setTextColor(0xFFFFFFFF) - menuView.setText("As the drawer opens, the drawer indicator icon becomes smaller.") - menuView.setGravity(Gravity.CENTER) - drawer.setMenuView(menuView) + listView.setAdapter(menuAdapter) + drawer.setMenuView(listView) // The drawable that replaces the up indicator in the action bar drawer.setSlideDrawable(R.drawable.ic_drawer) @@ -440,26 +443,44 @@ class Shadowsocks handler.sendEmptyMessage(MSG_INITIAL_FINISH) } } + } -/* override def onCreateOptionsMenu(menu: Menu): Boolean = { - val isRoot = status.getBoolean(Key.isRoot, false) - menu - .add(0, 0, 0, R.string.recovery) - .setIcon(android.R.drawable.ic_menu_revert) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT) - menu - .add(0, 1, 1, R.string.about) - .setIcon(android.R.drawable.ic_menu_info_details) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT) - if (isRoot || Build.VERSION.SDK_INT < 17) { - menu - .add(0, 2, 2, R.string.flush_dnscache) - .setIcon(android.R.drawable.ic_menu_revert) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT) - } - true - }*/ + def getProfileList: List[Item] = { + List[Item]() + } + + def getMenuList: List[Any] = { + + val buf = new ListBuffer[Any]() + + buf += new Category("Profiles") + + buf ++= getProfileList + + buf += new Category("Settings") + + buf += new Item(-1, getString(R.string.recovery), android.R.drawable.ic_menu_revert, + _ => { + EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "reset", getVersionName, 0L) + recovery() + }) + + buf += new Item(-2, getString(R.string.flush_dnscache), android.R.drawable.ic_menu_delete, + _ => { + EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "flush_dnscache", getVersionName, 0L) + flushDnsCache() + }) + + buf += new Item(-3, getString(R.string.about), android.R.drawable.ic_menu_info_details, + _ => { + EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "about", getVersionName, 0L) + showAbout() + }) + + buf.toList + + } override def onKeyDown(keyCode: Int, event: KeyEvent): Boolean = { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount == 0) { @@ -481,18 +502,6 @@ class Shadowsocks drawer.toggleMenu() return true } - case 0 => { - EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "reset", getVersionName, 0L) - recovery() - } - case 1 => { - EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "about", getVersionName, 0L) - showAbout() - } - case 2 => { - EasyTracker.getTracker.sendEvent(Shadowsocks.TAG, "flush_dnscache", getVersionName, 0L) - flushDnsCache() - } } super.onOptionsItemSelected(item) } diff --git a/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala b/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala new file mode 100644 index 00000000..1554d6a9 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala @@ -0,0 +1,113 @@ +package com.github.shadowsocks.database + +import android.content.{DialogInterface, Context} +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import com.github.shadowsocks.R +import android.view.View.OnClickListener + +case class Item(id: Int, title: String, iconRes: Int, action: Int => Unit) + +case class Category (title: String) + +object MenuAdapter { + + trait MenuListener { + def onActiveViewChanged(v: View) + } + + val UNDEFINED = -1 + val CATEGORY = 0 + val ITEM = 1 + +} + +class MenuAdapter (context: Context, var items: List[Any]) extends BaseAdapter { + + def setListener(listener: MenuAdapter.MenuListener) { + mListener = listener + } + + def setActivePosition(activePosition: Int) { + mActivePosition = activePosition + } + + @Override def getCount: Int = { + items.size + } + + @Override def getItem(position: Int): Object = { + items(position).asInstanceOf[Object] + } + + @Override def getItemId(position: Int): Long = { + position + } + + @Override override def getItemViewType(position: Int): Int = { + getItem(position) match { + case Category => MenuAdapter.CATEGORY + case Item => MenuAdapter.ITEM + case _ => MenuAdapter.UNDEFINED + } + } + + @Override override def getViewTypeCount: Int = { + 3 + } + + @Override override def isEnabled(position: Int): Boolean = { + getItemViewType(position) != MenuAdapter.UNDEFINED + } + + @Override override def areAllItemsEnabled: Boolean = { + false + } + + @Override def getView(position: Int, convertView: View, parent: ViewGroup): View = { + var v: View = convertView + val item = getItem(position) + item match { + case value: Category => + if (v == null) { + v = LayoutInflater.from(context).inflate(R.layout.menu_row_category, parent, false) + } + v.asInstanceOf[TextView].setText(value.title) + case value: Item => + if (v == null) { + v = LayoutInflater.from(context).inflate(R.layout.menu_row_item, parent, false) + } + val tv: TextView = v.asInstanceOf[TextView] + tv.setText(value.title) + tv.setCompoundDrawablesWithIntrinsicBounds(value.iconRes, 0, 0, 0) + tv.setOnClickListener(new OnClickListener { + def onClick(view: View) { + val item = view.getTag(R.id.mdItem).asInstanceOf[Item] + item.action(item.id) + } + }) + + v.setTag(R.id.mdItem, value) + case _ => + } + v.setTag(R.id.mdActiveViewPosition, position) + + if (position == mActivePosition) { + if (mListener != null) mListener.onActiveViewChanged(v) + } + v + } + + def updateList(list: List[Any]) { + items = list + notifyDataSetChanged() + } + + private var mListener: MenuAdapter.MenuListener = null + private var mActivePosition: Int = 0 +} + +