Commit 9511a616 authored by Max Lv's avatar Max Lv

add intent filter for shadowsocks URI #51

parent 1907ac2f
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks"> package="com.github.shadowsocks">
<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.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/> <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false"/> android:required="false"/>
<uses-sdk <uses-sdk
android:minSdkVersion="9" android:minSdkVersion="9"
android:targetSdkVersion="16"/> android:targetSdkVersion="16"/>
<application <application
android:name=".ShadowsocksApplication" android:name=".ShadowsocksApplication"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:backupAgent=".ShadowsocksBackupAgent" android:backupAgent=".ShadowsocksBackupAgent"
android:label="@string/app_name"> android:label="@string/app_name">
<meta-data android:name="com.google.android.backup.api_key" <meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/> android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
<activity <activity
android:name="com.google.ads.AdActivity" android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/> android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
<activity <activity
android:name=".Shadowsocks" android:name=".Shadowsocks"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.Shadowsocks" android:theme="@style/Theme.Shadowsocks"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".ShadowVpnActivity" android:name=".ParserActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" android:theme="@style/PopupTheme"
android:launchMode="singleTask"> android:launchMode="singleTask">
</activity> <intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="ss"/>
</intent-filter>
</activity>
<activity <activity
android:name=".AppManager" android:name=".ShadowVpnActivity"
android:theme="@style/Theme.AppManager" android:theme="@style/PopupTheme"
android:label="@string/app_name"> android:launchMode="singleTask">
<intent-filter> </activity>
<action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service <activity
android:name=".ShadowsocksService" android:name=".AppManager"
android:process=":proxy" android:theme="@style/Theme.AppManager"
android:exported="false"/> android:label="@string/app_name">
<intent-filter>
<action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<service <service
android:name=".ShadowVpnService" android:name=".ShadowsocksService"
android:label="@string/app_name" android:process=":proxy"
android:process=":vpn" android:exported="false"/>
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
<receiver android:name=".ShadowsocksReceiver"> <service
<intent-filter> android:name=".ShadowVpnService"
<action android:name="android.intent.action.BOOT_COMPLETED"/> android:label="@string/app_name"
<action android:name="com.github.shadowsocks.ACTION_UPDATE_STATE"/> android:process=":vpn"
</intent-filter> android:permission="android.permission.BIND_VPN_SERVICE"
</receiver> android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
</application> <receiver android:name=".ShadowsocksReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="com.github.shadowsocks.ACTION_UPDATE_STATE"/>
</intent-filter>
</receiver>
</application>
</manifest> </manifest>
...@@ -19,6 +19,15 @@ ...@@ -19,6 +19,15 @@
<resources> <resources>
<style name="PopupTheme" parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar">
<item name="android:windowIsFloating">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="Theme.Shadow" parent="@style/Theme.Sherlock.Light.DarkActionBar"> <style name="Theme.Shadow" parent="@style/Theme.Sherlock.Light.DarkActionBar">
<item name="android:actionBarItemBackground">@drawable/selectable_background_shadow</item> <item name="android:actionBarItemBackground">@drawable/selectable_background_shadow</item>
<item name="android:popupMenuStyle">@style/PopupMenu.Shadow</item> <item name="android:popupMenuStyle">@style/PopupMenu.Shadow</item>
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
<string name="add_profile">添加配置文件</string> <string name="add_profile">添加配置文件</string>
<!-- profile --> <!-- profile -->
<string name="add_profile_dialog">为影梭添加此配置文件?</string>
<string-array name="add_profile_methods"> <string-array name="add_profile_methods">
<item>扫描二维码</item> <item>扫描二维码</item>
<item>手动设置</item> <item>手动设置</item>
......
...@@ -84,6 +84,7 @@ ...@@ -84,6 +84,7 @@
<string name="add_profile">Add Profile</string> <string name="add_profile">Add Profile</string>
<!-- profile --> <!-- profile -->
<string name="add_profile_dialog">Add this Shadowsocks Profile?</string>
<string-array name="add_profile_methods"> <string-array name="add_profile_methods">
<item>Scan QR code</item> <item>Scan QR code</item>
<item>Manually Setting</item> <item>Manually Setting</item>
......
...@@ -19,7 +19,20 @@ ...@@ -19,7 +19,20 @@
<resources> <resources>
<style name="Theme.Shadow" parent="@style/Theme.Sherlock.Light.DarkActionBar"> <style name="PopupTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowCloseOnTouchOutside">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="windowContentOverlay">@null</item>
</style>
<style name="Theme.Shadow" parent="@style/Theme.Sherlock.Light.DarkActionBar">
<item name="actionBarItemBackground">@drawable/selectable_background_shadow</item> <item name="actionBarItemBackground">@drawable/selectable_background_shadow</item>
<item name="popupMenuStyle">@style/PopupMenu.Shadow</item> <item name="popupMenuStyle">@style/PopupMenu.Shadow</item>
<item name="dropDownListViewStyle">@style/DropDownListView.Shadow</item> <item name="dropDownListViewStyle">@style/DropDownListView.Shadow</item>
...@@ -30,7 +43,7 @@ ...@@ -30,7 +43,7 @@
<item name="actionModeSplitBackground">@drawable/cab_background_bottom_shadow</item> <item name="actionModeSplitBackground">@drawable/cab_background_bottom_shadow</item>
<item name="actionModeCloseButtonStyle">@style/ActionButton.CloseMode.Shadow</item> <item name="actionModeCloseButtonStyle">@style/ActionButton.CloseMode.Shadow</item>
<!-- Light.DarkActionBar specific --> <!-- Light.DarkActionBar specific -->
<item name="actionBarWidgetTheme">@style/Theme.Shadow.Widget</item> <item name="actionBarWidgetTheme">@style/Theme.Shadow.Widget</item>
</style> </style>
......
package com.github.shadowsocks
import android.os.{Message, Handler, Bundle}
import android.app.{ProgressDialog, AlertDialog, Activity}
import android.content.{Intent, DialogInterface}
import com.github.shadowsocks.database.{ProfileManager, Profile}
import com.github.shadowsocks.utils.{Parser, Action}
import android.preference.PreferenceManager
import android.view.WindowManager
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
class ParserActivity extends Activity {
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
showAsPopup(this)
val data = getIntent.getData.toString
new AlertDialog.Builder(this)
.setTitle(R.string.add_profile_dialog)
.setCancelable(false)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
Parser.parse(data) match {
case Some(profile) => addProfile(profile)
case _ => // ignore
}
dialog.dismiss()
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
override def onClick(dialog: DialogInterface, id: Int) {
dialog.dismiss()
finish()
}
})
.setMessage(data)
.create()
.show()
}
def showAsPopup(activity: Activity) {
activity.getWindow.setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
WindowManager.LayoutParams.FLAG_DIM_BEHIND)
val params = activity.getWindow.getAttributes
params.alpha = 1.0f
params.dimAmount = 0.5f
activity.getWindow.setAttributes(params.asInstanceOf[android.view.WindowManager.LayoutParams])
activity.getWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT))
}
def addProfile(profile: Profile) {
val h = showProgress(getString(R.string.loading))
h.postDelayed(new Runnable {
def run() {
val profileManager =
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper)
profileManager.createOrUpdateProfile(profile)
profileManager.reload(profile.id)
h.sendEmptyMessage(0)
}
}, 600)
}
private def showProgress(msg: String): Handler = {
val progressDialog = ProgressDialog.show(this, "", msg, true, false)
new Handler {
override def handleMessage(msg: Message) {
progressDialog.dismiss()
finish()
}
}
}
}
...@@ -46,6 +46,7 @@ import android.content.Intent ...@@ -46,6 +46,7 @@ import android.content.Intent
import android.util.Log import android.util.Log
import android.preference.PreferenceManager import android.preference.PreferenceManager
import com.github.shadowsocks.utils.Extra import com.github.shadowsocks.utils.Extra
import com.actionbarsherlock.app.SherlockActivity
class ShadowVpnActivity extends Activity { class ShadowVpnActivity extends Activity {
......
...@@ -579,10 +579,20 @@ class Shadowsocks ...@@ -579,10 +579,20 @@ class Shadowsocks
def onClick(dialog: DialogInterface, which: Int) { def onClick(dialog: DialogInterface, which: Int) {
which match { which match {
case 0 => { case 0 => {
val integrator = new IntentIntegrator(Shadowsocks.this) dialog.dismiss()
integrator.initiateScan() val h = showProgress(getString(R.string.loading))
h.postDelayed(new Runnable() {
def run() {
val integrator = new IntentIntegrator(Shadowsocks.this)
integrator.initiateScan()
h.sendEmptyMessage(0)
}
}, 600)
}
case 1 => {
dialog.dismiss()
addProfile(id)
} }
case 1 => addProfile(id)
case _ => case _ =>
} }
} }
...@@ -874,7 +884,7 @@ class Shadowsocks ...@@ -874,7 +884,7 @@ class Shadowsocks
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) {
ShadowParser.parse(scanResult.getContents) match { Parser.parse(scanResult.getContents) match {
case Some(profile) => addProfile(profile) case Some(profile) => addProfile(profile)
case _ => // ignore case _ => // ignore
} }
......
...@@ -30,7 +30,7 @@ object Key { ...@@ -30,7 +30,7 @@ object Key {
object Scheme { object Scheme {
val APP = "app://" val APP = "app://"
val PROFILE = "profile://" val PROFILE = "profile://"
val SHADOW = "shadow:" val SS = "ss"
} }
object State { object State {
......
...@@ -4,14 +4,14 @@ import com.github.shadowsocks.database.Profile ...@@ -4,14 +4,14 @@ import com.github.shadowsocks.database.Profile
import android.net.Uri import android.net.Uri
import android.util.{Log, Base64} import android.util.{Log, Base64}
object ShadowParser { object Parser {
val TAG = "ShadowParser" val TAG = "ShadowParser"
def parse (data: String): Option[Profile] = { def parse (data: String): Option[Profile] = {
try { try {
Log.d(TAG, data) Log.d(TAG, data)
val uri = Uri.parse(data.trim) val uri = Uri.parse(data.trim)
if (uri.isOpaque && uri.getScheme == "shadow") { if (uri.isOpaque && uri.getScheme == Scheme.SS) {
val encoded = data.replace("shadow:", "") val encoded = data.replace(Scheme.SS + ":", "")
val content = new String(Base64.decode(encoded, Base64.NO_PADDING), "UTF-8") val content = new String(Base64.decode(encoded, Base64.NO_PADDING), "UTF-8")
val info = content.split('@') val info = content.split('@')
val encinfo = info(0).split(':') val encinfo = info(0).split(':')
......
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