Commit e99a0eaa authored by Max Lv's avatar Max Lv

Merge pull request #609 from AnyOfYou/master

Fix parser bug, add share profile by NFC.
parents 78e57de8 7f2f67f2
...@@ -6,11 +6,14 @@ ...@@ -6,11 +6,14 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.NFC" />
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false"/> android:required="false"/>
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="16"
android:targetSdkVersion="23"/> android:targetSdkVersion="23"/>
...@@ -46,13 +49,20 @@ ...@@ -46,13 +49,20 @@
<activity <activity
android:name=".ParserActivity" android:name=".ParserActivity"
android:theme="@style/PopupTheme" android:theme="@style/PopupTheme"
android:launchMode="singleTask"> android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleInstance">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW"/> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/> <data android:scheme="ss"/>
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="ss" />
</intent-filter>
</activity> </activity>
<activity <activity
......
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
android:drawablePadding="20dp" android:drawablePadding="20dp"
android:maxLines="4" android:maxLines="4"
android:scrollHorizontally="false" /> android:scrollHorizontally="false" />
<ImageView android:id="@+id/qrcode" <ImageView android:id="@+id/share"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="5dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:src="@drawable/ic_social_share" android:src="@drawable/ic_social_share"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/qrcode"/> android:contentDescription="@string/share"/>
</LinearLayout> </LinearLayout>
...@@ -60,7 +60,8 @@ ...@@ -60,7 +60,8 @@
<string name="flush_dnscache_summary_root">只需在 NAT 模式下使用,需要 ROOT 权限</string> <string name="flush_dnscache_summary_root">只需在 NAT 模式下使用,需要 ROOT 权限</string>
<string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string> <string name="flush_dnscache_no_root">在没有 ROOT 权限的情况下无法刷新 DNS 缓存</string>
<string name="flushing">刷新中……</string> <string name="flushing">刷新中……</string>
<string name="qrcode">二维码</string> <string name="share">分享</string>
<string name="share_message">扫描二维码或者使用 NFC(Android Beam)</string>
<string name="add_profile">添加配置文件</string> <string name="add_profile">添加配置文件</string>
<string name="nat">NAT 模式 (仅限调试)</string> <string name="nat">NAT 模式 (仅限调试)</string>
......
...@@ -91,7 +91,8 @@ ...@@ -91,7 +91,8 @@
<string name="recovery_summary">Reset all the background services</string> <string name="recovery_summary">Reset all the background services</string>
<string name="about">About</string> <string name="about">About</string>
<string name="about_title">Shadowsocks %s</string> <string name="about_title">Shadowsocks %s</string>
<string name="qrcode">QR Code</string> <string name="share">Share</string>
<string name="share_message">Scan QRCode or Use NFC (Android Beam)</string>
<string name="flush_dnscache">Flush DNS cache</string> <string name="flush_dnscache">Flush DNS cache</string>
<string name="flush_dnscache_summary">Only useful in NAT mode</string> <string name="flush_dnscache_summary">Only useful in NAT mode</string>
<string name="flush_dnscache_summary_root">Only useful in NAT mode. Requires ROOT permission.</string> <string name="flush_dnscache_summary_root">Only useful in NAT mode. Requires ROOT permission.</string>
......
...@@ -39,10 +39,11 @@ ...@@ -39,10 +39,11 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.content.DialogInterface import android.content.{Intent, DialogInterface}
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.nfc.{NdefMessage, NfcAdapter}
import android.os.{Parcelable, Bundle}
import android.support.v7.app.{AppCompatActivity, AlertDialog} import android.support.v7.app.{AppCompatActivity, AlertDialog}
import android.view.WindowManager import android.view.WindowManager
import com.github.shadowsocks.utils.Parser import com.github.shadowsocks.utils.Parser
...@@ -50,8 +51,24 @@ import com.github.shadowsocks.utils.Parser ...@@ -50,8 +51,24 @@ import com.github.shadowsocks.utils.Parser
class ParserActivity extends AppCompatActivity { class ParserActivity extends AppCompatActivity {
override def onCreate(savedInstanceState: Bundle) { override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val profiles = Parser.findAll(getIntent.getData.toString) }
if (profiles.isEmpty) { // ignore
override def onResume() {
super.onResume()
var sharedStr = ""
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent.getAction)) {
val rawMsgs = getIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
if (rawMsgs != null) {
val msg = rawMsgs(0).asInstanceOf[NdefMessage]
val records = msg.getRecords
sharedStr = new String(records(0).getPayload)
}
} else if (Intent.ACTION_VIEW.equals(getIntent.getAction)) {
sharedStr = getIntent.getData.toString
}
val profiles = Parser.findAll(sharedStr)
if (profiles.isEmpty) {
finish() finish()
return return
} }
...@@ -62,9 +79,9 @@ class ParserActivity extends AppCompatActivity { ...@@ -62,9 +79,9 @@ class ParserActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.yes, ((_, _) => .setPositiveButton(android.R.string.yes, ((_, _) =>
profiles.foreach(ShadowsocksApplication.profileManager.createProfile)): DialogInterface.OnClickListener) profiles.foreach(ShadowsocksApplication.profileManager.createProfile)): DialogInterface.OnClickListener)
.setNegativeButton(android.R.string.no, null) .setNegativeButton(android.R.string.no, null)
.setMessage(profiles.mkString("\n")) .setMessage(sharedStr)
.create() .create()
dialog.setOnDismissListener((dialog: DialogInterface) => finish()) // builder method was added in API 17 dialog.setOnDismissListener((dialog: DialogInterface) => finish()) // builder method was added in API 17
dialog.show() dialog.show()
} }
......
package com.github.shadowsocks package com.github.shadowsocks
import java.nio.charset.Charset
import android.content._ import android.content._
import android.nfc.{NdefRecord, NdefMessage, NfcEvent, NfcAdapter}
import android.nfc.NfcAdapter.CreateNdefMessageCallback
import android.os.{Bundle, Handler} import android.os.{Bundle, Handler}
import android.support.v7.app.{AlertDialog, AppCompatActivity} import android.support.v7.app.{AlertDialog, AppCompatActivity}
import android.support.v7.widget.RecyclerView.ViewHolder import android.support.v7.widget.RecyclerView.ViewHolder
...@@ -9,7 +13,7 @@ import android.support.v7.widget.helper.ItemTouchHelper ...@@ -9,7 +13,7 @@ import android.support.v7.widget.helper.ItemTouchHelper
import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback
import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar} import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar}
import android.text.style.TextAppearanceSpan import android.text.style.TextAppearanceSpan
import android.text.{SpannableStringBuilder, Spanned} import android.text.{TextUtils, SpannableStringBuilder, Spanned}
import android.view.{LayoutInflater, MenuItem, View, ViewGroup} import android.view.{LayoutInflater, MenuItem, View, ViewGroup}
import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast} import android.widget.{CheckedTextView, ImageView, LinearLayout, Toast}
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
...@@ -24,16 +28,20 @@ import scala.collection.mutable.ArrayBuffer ...@@ -24,16 +28,20 @@ import scala.collection.mutable.ArrayBuffer
/** /**
* @author Mygod * @author Mygod
*/ */
class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener with ServiceBoundContext { class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener with ServiceBoundContext with CreateNdefMessageCallback{
private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener {
var item: Profile = _ var item: Profile = _
private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView] private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView]
itemView.setOnClickListener(this) itemView.setOnClickListener(this)
{ {
val qrcode = itemView.findViewById(R.id.qrcode) val shareBtn = itemView.findViewById(R.id.share)
qrcode.setOnClickListener(_ => { shareBtn.setOnClickListener(_ => {
val url = Parser.generate(item) val url = Parser.generate(item)
if (nfcAdapter != null && nfcAdapter.isEnabled) {
nfcAdapter.setNdefPushMessageCallback(ProfileManagerActivity.this,ProfileManagerActivity.this)
nfcShareItem = url
}
val image = new ImageView(ProfileManagerActivity.this) val image = new ImageView(ProfileManagerActivity.this)
image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)) image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1))
val qrcode = QRCode.from(url) val qrcode = QRCode.from(url)
...@@ -41,17 +49,24 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -41,17 +49,24 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
.asInstanceOf[QRCode].bitmap() .asInstanceOf[QRCode].bitmap()
image.setImageBitmap(qrcode) image.setImageBitmap(qrcode)
new AlertDialog.Builder(ProfileManagerActivity.this) val dialog = new AlertDialog.Builder(ProfileManagerActivity.this)
.setCancelable(true) .setCancelable(true)
.setPositiveButton(R.string.close, null) .setPositiveButton(R.string.close, null)
.setNegativeButton(R.string.copy_url, ((_, _) => .setNegativeButton(R.string.copy_url, ((_, _) =>
clipboard.setPrimaryClip(ClipData.newPlainText(null, url))): DialogInterface.OnClickListener) clipboard.setPrimaryClip(ClipData.newPlainText(null, url))): DialogInterface.OnClickListener)
.setView(image) .setView(image)
.setMessage(R.string.share_message)
.setTitle(R.string.share)
.create() .create()
.show() dialog.show()
dialog.setOnDismissListener((dialog : DialogInterface) => {
if (nfcAdapter != null && nfcAdapter.isEnabled) {
nfcAdapter.setNdefPushMessageCallback(null,ProfileManagerActivity.this)
}
})
}) })
qrcode.setOnLongClickListener((v: View) => { shareBtn.setOnLongClickListener((v: View) => {
Utils.positionToast(Toast.makeText(ProfileManagerActivity.this, R.string.qrcode, Toast.LENGTH_SHORT), qrcode, Utils.positionToast(Toast.makeText(ProfileManagerActivity.this, R.string.share, Toast.LENGTH_SHORT), shareBtn,
getWindow, 0, Utils.dpToPx(ProfileManagerActivity.this, 8)).show getWindow, 0, Utils.dpToPx(ProfileManagerActivity.this, 8)).show
true true
}) })
...@@ -148,6 +163,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -148,6 +163,8 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
private var undoManager: UndoSnackbarManager[Profile] = _ private var undoManager: UndoSnackbarManager[Profile] = _
private lazy val clipboard = getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager] private lazy val clipboard = getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager]
private var nfcAdapter : NfcAdapter = _
private var nfcShareItem : String = _
override def onCreate(savedInstanceState: Bundle) { override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -163,6 +180,11 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -163,6 +180,11 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
toolbar.inflateMenu(R.menu.profile_manager_menu) toolbar.inflateMenu(R.menu.profile_manager_menu)
toolbar.setOnMenuItemClickListener(this) toolbar.setOnMenuItemClickListener(this)
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
if (nfcAdapter != null && nfcAdapter.isEnabled) {
nfcAdapter.setNdefPushMessageCallback(null,ProfileManagerActivity.this)
}
ShadowsocksApplication.profileManager.setProfileAddedListener(profilesAdapter.add) ShadowsocksApplication.profileManager.setProfileAddedListener(profilesAdapter.add)
val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView] val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView]
val layoutManager = new LinearLayoutManager(this) val layoutManager = new LinearLayoutManager(this)
...@@ -217,10 +239,16 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe ...@@ -217,10 +239,16 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (scanResult != null) if (scanResult != null && !TextUtils.isEmpty(scanResult.getContents))
Parser.findAll(scanResult.getContents).foreach(ShadowsocksApplication.profileManager.createProfile) Parser.findAll(scanResult.getContents).foreach(ShadowsocksApplication.profileManager.createProfile)
} }
def createNdefMessage(nfcEvent: NfcEvent): NdefMessage = {
val ndefRecord = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI,nfcShareItem.getBytes(Charset.forName("US-ASCII")),Array[Byte](),nfcShareItem.getBytes(Charset.forName("US-ASCII")))
val ndefMessage = new NdefMessage(Array[NdefRecord](ndefRecord))
ndefMessage
}
def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match { def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match {
case R.id.scan_qr_code => case R.id.scan_qr_code =>
val integrator = new IntentIntegrator(this) val integrator = new IntentIntegrator(this)
......
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