From 29c5011ce4c689b4392ecb721f5ff8beae8c3d7c Mon Sep 17 00:00:00 2001 From: Lucas Liu Date: Sat, 2 Apr 2016 02:12:55 -0500 Subject: [PATCH] Refine UI. --- build.sbt | 3 +- src/main/res/drawable/ic_camera_alt.xml | 9 ++ src/main/res/drawable/ic_content_copy.xml | 9 ++ src/main/res/drawable/ic_content_paste.xml | 9 ++ src/main/res/drawable/ic_create.xml | 9 ++ src/main/res/drawable/ic_file_download.xml | 9 ++ src/main/res/drawable/ic_file_upload.xml | 9 ++ src/main/res/drawable/ic_nfc.xml | 9 ++ src/main/res/layout/layout_profiles.xml | 64 ++++++++++++- src/main/res/menu/profile_manager_menu.xml | 28 +----- src/main/res/values-zh-rCN/strings.xml | 2 + src/main/res/values/strings.xml | 2 + .../shadowsocks/ProfileManagerActivity.scala | 91 ++++++++++++++----- 13 files changed, 201 insertions(+), 52 deletions(-) create mode 100644 src/main/res/drawable/ic_camera_alt.xml create mode 100644 src/main/res/drawable/ic_content_copy.xml create mode 100644 src/main/res/drawable/ic_content_paste.xml create mode 100644 src/main/res/drawable/ic_create.xml create mode 100644 src/main/res/drawable/ic_file_download.xml create mode 100644 src/main/res/drawable/ic_file_upload.xml create mode 100644 src/main/res/drawable/ic_nfc.xml diff --git a/build.sbt b/build.sbt index 8fea1e81..708e76e7 100644 --- a/build.sbt +++ b/build.sbt @@ -42,7 +42,8 @@ libraryDependencies ++= Seq( "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", "com.j256.ormlite" % "ormlite-core" % "4.48", "com.j256.ormlite" % "ormlite-android" % "4.48", - "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" + "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2", + "com.github.clans" % "fab" % "1.6.2" ) proguardOptions in Android ++= Seq("-keep class com.github.shadowsocks.** { (...); }", diff --git a/src/main/res/drawable/ic_camera_alt.xml b/src/main/res/drawable/ic_camera_alt.xml new file mode 100644 index 00000000..ff3a931e --- /dev/null +++ b/src/main/res/drawable/ic_camera_alt.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_content_copy.xml b/src/main/res/drawable/ic_content_copy.xml new file mode 100644 index 00000000..c384af53 --- /dev/null +++ b/src/main/res/drawable/ic_content_copy.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_content_paste.xml b/src/main/res/drawable/ic_content_paste.xml new file mode 100644 index 00000000..9507c7f4 --- /dev/null +++ b/src/main/res/drawable/ic_content_paste.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_create.xml b/src/main/res/drawable/ic_create.xml new file mode 100644 index 00000000..3ca8b43c --- /dev/null +++ b/src/main/res/drawable/ic_create.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_file_download.xml b/src/main/res/drawable/ic_file_download.xml new file mode 100644 index 00000000..7b463f08 --- /dev/null +++ b/src/main/res/drawable/ic_file_download.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_file_upload.xml b/src/main/res/drawable/ic_file_upload.xml new file mode 100644 index 00000000..9edf6e49 --- /dev/null +++ b/src/main/res/drawable/ic_file_upload.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable/ic_nfc.xml b/src/main/res/drawable/ic_nfc.xml new file mode 100644 index 00000000..fc27d645 --- /dev/null +++ b/src/main/res/drawable/ic_nfc.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/layout/layout_profiles.xml b/src/main/res/layout/layout_profiles.xml index 500b67f9..3e178624 100644 --- a/src/main/res/layout/layout_profiles.xml +++ b/src/main/res/layout/layout_profiles.xml @@ -1,16 +1,74 @@ + android:layout_height="match_parent"> + + + + + + + + diff --git a/src/main/res/menu/profile_manager_menu.xml b/src/main/res/menu/profile_manager_menu.xml index a5f305df..14924d01 100644 --- a/src/main/res/menu/profile_manager_menu.xml +++ b/src/main/res/menu/profile_manager_menu.xml @@ -1,28 +1,8 @@ - - - - - - - - + diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 5abb0486..b794e265 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -97,6 +97,8 @@ 为影梭添加此配置文件? 扫描二维码 手动设置 + 使用 Android Beam (NFC) 来添加配置 + 保持两台设备的屏幕亮起,在另外一台设备上,点击配置文件的分享按钮,将它们靠在一起(背靠背)。\n你也可以扫描 NFC 标签。 全局 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 486bb20c..0dbca08b 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -117,6 +117,8 @@ Add this Shadowsocks Profile? Scan QR code Manual Settings + Use Android Beam (NFC) to add profile + Keep both devices screen on, on the other device, click profile\'s share button, bring them together (back to back).\nYou also can scan a nfc tag. Copy URL Removed diff --git a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala index 0bd1efe7..bb99ae85 100644 --- a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala @@ -17,6 +17,7 @@ import android.text.style.TextAppearanceSpan import android.text.{SpannableStringBuilder, Spanned, TextUtils} import android.view.{LayoutInflater, MenuItem, View, ViewGroup} import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast} +import com.github.clans.fab.{FloatingActionMenu, FloatingActionButton} import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.database.Profile import com.github.shadowsocks.utils.{Key, Parser, TrafficMonitor, Utils} @@ -26,7 +27,7 @@ import net.glxn.qrgen.android.QRCode import scala.collection.mutable.ArrayBuffer -class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener with ServiceBoundContext +class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener with ServiceBoundContext with View.OnClickListener with CreateNdefMessageCallback { private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { var item: Profile = _ @@ -162,6 +163,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe private var selectedItem: ProfileViewHolder = _ private val handler = new Handler + private var menu : FloatingActionMenu = _ + private lazy val profilesAdapter = new ProfilesAdapter private var undoManager: UndoSnackbarManager[Profile] = _ @@ -187,6 +190,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe toolbar.inflateMenu(R.menu.profile_manager_menu) toolbar.setOnMenuItemClickListener(this) + initFab() + ShadowsocksApplication.profileManager.setProfileAddedListener(profilesAdapter.add) val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView] val layoutManager = new LinearLayoutManager(this) @@ -226,6 +231,19 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe if (intent != null) handleShareIntent(intent) } + def initFab() { + menu = findViewById(R.id.menu).asInstanceOf[FloatingActionMenu] + menu.setClosedOnTouchOutside(true) + val manualAddFAB = findViewById(R.id.fab_manual_add).asInstanceOf[FloatingActionButton] + manualAddFAB.setOnClickListener(this) + val qrcodeAddFAB = findViewById(R.id.fab_qrcode_add).asInstanceOf[FloatingActionButton] + qrcodeAddFAB.setOnClickListener(this) + val nfcAddFAB = findViewById(R.id.fab_nfc_add).asInstanceOf[FloatingActionButton] + nfcAddFAB.setOnClickListener(this) + val importAddFAB = findViewById(R.id.fab_import_add).asInstanceOf[FloatingActionButton] + importAddFAB.setOnClickListener(this) + } + override def onResume() { super.onResume() @@ -237,6 +255,50 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe handleShareIntent(intent) } + override def onClick(v: View){ + v.getId match { + case R.id.fab_manual_add => + menu.toggle(true) + ShadowsocksApplication.profileManager.reload(-1) + ShadowsocksApplication.switchProfile(ShadowsocksApplication.profileManager.save.id) + finish + case R.id.fab_qrcode_add => + menu.toggle(false) + val integrator = new IntentIntegrator(ProfileManagerActivity.this) + val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN) + list.add("tw.com.quickmark") + integrator.setTargetApplications(list) + integrator.initiateScan() + case R.id.fab_nfc_add => + menu.toggle(true) + val dialog = new AlertDialog.Builder(ProfileManagerActivity.this) + .setCancelable(true) + .setPositiveButton(R.string.gotcha, null) + .setTitle(R.string.add_profile_nfc_hint_title) + .create() + if (!isNfcBeamEnabled) { + dialog.setMessage(getString(R.string.share_message_nfc_disabled)) + dialog.setButton(DialogInterface.BUTTON_NEUTRAL, getString(R.string.turn_on_nfc), ((_, _) => + startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)) + ): DialogInterface.OnClickListener) + } else { + dialog.setMessage(getString(R.string.add_profile_nfc_hint)) + } + dialog.show + case R.id.fab_import_add => + menu.toggle(true) + if (clipboard.hasPrimaryClip) { + val profiles = Parser.findAll(clipboard.getPrimaryClip.getItemAt(0).getText) + if (profiles.nonEmpty) { + profiles.foreach(ShadowsocksApplication.profileManager.createProfile) + Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show + return + } + } + Toast.makeText(this, R.string.action_import_err, Toast.LENGTH_SHORT).show + } + } + def updateNfcState() { isNfcAvailable = false isNfcEnabled = false @@ -305,33 +367,14 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe } } + override def onBackPressed() { + if (menu.isOpened) menu.close(true) else super.onBackPressed() + } + def createNdefMessage(nfcEvent: NfcEvent) = new NdefMessage(Array(new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))) def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match { - case R.id.scan_qr_code => - val integrator = new IntentIntegrator(this) - val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN) - list.add("tw.com.quickmark") - integrator.setTargetApplications(list) - integrator.initiateScan() - true - case R.id.manual_settings => - ShadowsocksApplication.profileManager.reload(-1) - ShadowsocksApplication.switchProfile(ShadowsocksApplication.profileManager.save.id) - finish - true - case R.id.action_import => - if (clipboard.hasPrimaryClip) { - val profiles = Parser.findAll(clipboard.getPrimaryClip.getItemAt(0).getText) - if (profiles.nonEmpty) { - profiles.foreach(ShadowsocksApplication.profileManager.createProfile) - Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show - return true - } - } - Toast.makeText(this, R.string.action_import_err, Toast.LENGTH_SHORT).show - true case R.id.action_export => ShadowsocksApplication.profileManager.getAllProfiles match { case Some(profiles) =>