Commit f891bd17 authored by Max Lv's avatar Max Lv

Merge pull request #456 from CzBiX/tasker

Automation app integration
parents 1de3a449 a6c12dfd
......@@ -39,7 +39,8 @@ libraryDependencies ++= Seq(
"com.android.support" % "design" % "23.1.1",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.48"
"com.j256.ormlite" % "ormlite-android" % "4.48",
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2"
)
proguardOptions in Android ++= Seq("-keep class com.github.shadowsocks.** { <init>(...); }",
......
......@@ -80,6 +80,14 @@
android:parentActivityName=".Shadowsocks"
android:launchMode="singleTask"/>
<activity android:name=".TaskerActivity"
android:icon="@drawable/ic_launcher"
android:launchMode="singleTask">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
</intent-filter>
</activity>
<service
android:name=".ShadowsocksRunnerService"
android:exported="false">
......@@ -109,6 +117,11 @@
</intent-filter>
</receiver>
<receiver android:name=".TaskerReceiver">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
</intent-filter>
</receiver>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_light_dark" />
<RelativeLayout
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/toggle_service_state"
android:id="@+id/service_switch"
android:layout_centerHorizontal="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save"
android:textColor="@android:color/white"
android:id="@+id/btn_save"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
......@@ -97,5 +97,7 @@
<string name="removed">已删除</string>
<string name="undo">撤销</string>
<string name="toggle_service_state">切换服务状态</string>
<string name="save">保存</string>
</resources>
......@@ -102,4 +102,10 @@
<string name="removed">Removed</string>
<string name="undo">Undo</string>
<!-- tasker -->
<string name="toggle_service_state">Toggle service state</string>
<string name="save">Save</string>
<string name="turn_service_state">Turn service %s</string>
<string name="state_on">On</string>
<string name="state_off">Off</string>
</resources>
......@@ -47,18 +47,8 @@ class ShadowsocksReceiver extends BroadcastReceiver {
val TAG = "Shadowsocks"
def onReceive(context: Context, intent: Intent) {
val isAutoConnect: Boolean = ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)
val isInstalled: Boolean = ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)
if (isAutoConnect && isInstalled) {
if (Utils.isLollipopOrAbove) {
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
context.startService(intent)
} else {
val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(intent)
}
if (ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false)) {
Utils.startSsService(context)
}
}
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.util.Log
import android.view.View
import android.widget.{Button, Switch}
import com.github.shadowsocks.helper.TaskerSettings
import com.twofortyfouram.locale.api.{Intent => ApiIntent}
/**
* @author CzBiX
*/
class TaskerActivity extends AppCompatActivity {
var taskerOption: TaskerSettings = _
var switch: Switch = _
var btnSave: Button = _
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_tasker)
switch = findViewById(R.id.service_switch).asInstanceOf[Switch]
btnSave = findViewById(R.id.btn_save).asInstanceOf[Button]
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
toolbar.setTitle(R.string.screen_name)
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)
toolbar.setNavigationOnClickListener(_ => finish())
switch.setOnCheckedChangeListener((_, isChecked) => taskerOption.is_start = isChecked)
btnSave.setOnClickListener(_ => saveResult())
loadSettings()
}
private def loadSettings(): Unit = {
val intent: Intent = getIntent
if (intent.getAction != ApiIntent.ACTION_EDIT_SETTING) {
Log.w(Shadowsocks.TAG, "unknown tasker action")
finish()
return
}
taskerOption = TaskerSettings.fromIntent(intent)
switch.setChecked(taskerOption.is_start)
}
private def saveResult(): Unit = {
setResult(Activity.RESULT_OK, taskerOption.toIntent(this))
finish()
}
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks
import android.content.{BroadcastReceiver, Context, Intent}
import com.github.shadowsocks.helper.TaskerSettings
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)
if (settings.is_start) {
Utils.startSsService(context)
} else {
Utils.stopSsService(context)
}
}
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.helper
import android.content.{Intent, Context}
import android.os.Bundle
import com.github.shadowsocks.R
import com.twofortyfouram.locale.api.{Intent => ApiIntent}
object TaskerSettings {
def fromIntent(intent: Intent): TaskerSettings = {
val bundle: Bundle = 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)
}
}
class TaskerSettings(bundle: Bundle) {
val KEY_IS_START = "is_start"
var is_start: Boolean = bundle.getBoolean(KEY_IS_START)
def toIntent(context: Context): Intent = {
val bundle = new Bundle()
bundle.putBoolean(KEY_IS_START, is_start)
val desc = context.getString(R.string.turn_service_state,
context.getString(if (is_start) R.string.state_on else R.string.state_off))
val intent: Intent = new Intent()
intent.putExtra(ApiIntent.EXTRA_STRING_BLURB, desc)
.putExtra(ApiIntent.EXTRA_BUNDLE, bundle)
}
}
......@@ -55,32 +55,18 @@ import android.util.{DisplayMetrics, Base64, Log}
import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window}
import android.widget.Toast
import com.github.shadowsocks.{ShadowsocksApplication, BuildConfig}
import com.github.shadowsocks.{ShadowsocksRunnerActivity, ShadowsocksRunnerService, ShadowsocksApplication, BuildConfig}
import org.xbill.DNS._
object Utils {
val TAG: String = "Shadowsocks"
val ABI_PROP: String = "ro.product.cpu.abi"
val ABI2_PROP: String = "ro.product.cpu.abi2"
val ARM_ABI: String = "armeabi"
val ARMV7_ABI: String = "armeabi-v7a"
val X86_ABI: String = "x86"
val MIPS_ABI: String = "mips"
val DEFAULT_SHELL: String = "/system/bin/sh"
val DEFAULT_ROOT: String = "/system/bin/su"
val ALTERNATIVE_ROOT: String = "/system/xbin/su"
val DEFAULT_IPTABLES: String = "/system/bin/iptables"
val ALTERNATIVE_IPTABLES: String = "iptables"
val TIME_OUT: Int = -99
var initialized: Boolean = false
var hasRedirectSupport: Int = -1
var shell: String = null
var root_shell: String = null
var iptables: String = null
var data_path: String = null
var rootTries = 0
def isLollipopOrAbove: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
......@@ -397,7 +383,6 @@ object Utils {
if (!Console.isRoot) return
hasRedirectSupport = 1
val sb = new StringBuilder
val command = Utils.getIptables + " -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
val lines = Console.runRootCommand(command)
......@@ -416,6 +401,25 @@ object Utils {
false
}
}
def startSsService(context: Context): Unit = {
val isInstalled: Boolean = ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)
if (!isInstalled) return
if (Utils.isLollipopOrAbove) {
val intent = new Intent(context, classOf[ShadowsocksRunnerService])
context.startService(intent)
} else {
val intent = new Intent(context, classOf[ShadowsocksRunnerActivity])
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(intent)
}
}
def stopSsService(context: Context): Unit = {
context.sendBroadcast(new Intent(Action.CLOSE))
}
}
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