Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a40f87f492 | ||
|
|
56f4a36a18 | ||
|
|
6db818cc46 | ||
|
|
c86b68c0e6 | ||
|
|
56728a3f40 | ||
|
|
cc070223b4 | ||
|
|
0335d67dba | ||
|
|
0d0c2d59de | ||
|
|
16ee0edea8 | ||
|
|
e03c34aa19 | ||
|
|
db011afcc0 | ||
|
|
ebe4d6fd0c | ||
|
|
cb9356e5e0 | ||
|
|
c55fd2dd65 | ||
|
|
3c3406d3c7 | ||
|
|
e603700587 |
+1
-1
@@ -24,7 +24,7 @@ install:
|
|||||||
- popd
|
- popd
|
||||||
- touch $HOME/.android/repositories.cfg
|
- touch $HOME/.android/repositories.cfg
|
||||||
- yes | sdkmanager
|
- yes | sdkmanager
|
||||||
"build-tools;27.0.2"
|
"build-tools;27.0.3"
|
||||||
ndk-bundle
|
ndk-bundle
|
||||||
"platforms;android-27"
|
"platforms;android-27"
|
||||||
>/dev/null
|
>/dev/null
|
||||||
|
|||||||
+2
-2
@@ -5,10 +5,10 @@ buildscript {
|
|||||||
kotlinVersion = '1.2.10'
|
kotlinVersion = '1.2.10'
|
||||||
minSdkVersion = 19
|
minSdkVersion = 19
|
||||||
sdkVersion = 27
|
sdkVersion = 27
|
||||||
buildToolsVersion = '27.0.2'
|
buildToolsVersion = '27.0.3'
|
||||||
supportLibraryVersion = '27.0.2'
|
supportLibraryVersion = '27.0.2'
|
||||||
takisoftFixVersion = '27.0.2.0'
|
takisoftFixVersion = '27.0.2.0'
|
||||||
playServicesVersion = '11.6.2'
|
playServicesVersion = '11.8.0'
|
||||||
junitVersion = '4.12'
|
junitVersion = '4.12'
|
||||||
androidTestVersion = '1.0.1'
|
androidTestVersion = '1.0.1'
|
||||||
androidEspressoVersion = '3.0.1'
|
androidEspressoVersion = '3.0.1'
|
||||||
|
|||||||
Submodule core/src/main/jni/shadowsocks-libev updated: 0c3cf8beb3...cba94db423
+30
-25
@@ -56,33 +56,38 @@ export GOROOT=$DIR/go
|
|||||||
export GOPATH=$DIR
|
export GOPATH=$DIR
|
||||||
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
|
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
|
||||||
|
|
||||||
echo "Get dependences for overture"
|
if [ ! -f "$TARGET/armeabi-v7a/liboverture.so" ] || [ ! -f "$TARGET/arm64-v8a/liboverture.so" ] ||
|
||||||
go get -u github.com/tools/godep
|
[ ! -f "$TARGET/x86/liboverture.so" ]; then
|
||||||
|
|
||||||
pushd $GOPATH/src/github.com/shadowsocks/overture/main
|
echo "Get dependences for overture"
|
||||||
godep restore
|
go get -u github.com/tools/godep
|
||||||
|
|
||||||
|
pushd $GOPATH/src/github.com/shadowsocks/overture/main
|
||||||
|
godep restore
|
||||||
|
|
||||||
|
echo "Cross compile overture for arm"
|
||||||
|
if [ ! -f "$TARGET/armeabi-v7a/liboverture.so" ]; then
|
||||||
|
try env CGO_ENABLED=1 CC=$ANDROID_ARM_CC GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-s -w"
|
||||||
|
try $ANDROID_ARM_STRIP main
|
||||||
|
try mv main $TARGET/armeabi-v7a/liboverture.so
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Cross compile overture for arm64"
|
||||||
|
if [ ! -f "$TARGET/arm64-v8a/liboverture.so" ]; then
|
||||||
|
try env CGO_ENABLED=1 CC=$ANDROID_ARM64_CC GOOS=android GOARCH=arm64 go build -ldflags="-s -w"
|
||||||
|
try $ANDROID_ARM64_STRIP main
|
||||||
|
try mv main $TARGET/arm64-v8a/liboverture.so
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Cross compile overture for x86"
|
||||||
|
if [ ! -f "$TARGET/x86/liboverture.so" ]; then
|
||||||
|
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build -ldflags="-s -w"
|
||||||
|
try $ANDROID_X86_STRIP main
|
||||||
|
try mv main $TARGET/x86/liboverture.so
|
||||||
|
fi
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
echo "Cross compile overture for arm"
|
|
||||||
if [ ! -f "$TARGET/armeabi-v7a/liboverture.so" ]; then
|
|
||||||
try env CGO_ENABLED=1 CC=$ANDROID_ARM_CC GOOS=android GOARCH=arm GOARM=7 go build -ldflags="-s -w"
|
|
||||||
try $ANDROID_ARM_STRIP main
|
|
||||||
try mv main $TARGET/armeabi-v7a/liboverture.so
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Cross compile overture for arm64"
|
|
||||||
if [ ! -f "$TARGET/arm64-v8a/liboverture.so" ]; then
|
|
||||||
try env CGO_ENABLED=1 CC=$ANDROID_ARM64_CC GOOS=android GOARCH=arm64 go build -ldflags="-s -w"
|
|
||||||
try $ANDROID_ARM64_STRIP main
|
|
||||||
try mv main $TARGET/arm64-v8a/liboverture.so
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Cross compile overture for x86"
|
|
||||||
if [ ! -f "$TARGET/x86/liboverture.so" ]; then
|
|
||||||
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build -ldflags="-s -w"
|
|
||||||
try $ANDROID_X86_STRIP main
|
|
||||||
try mv main $TARGET/x86/liboverture.so
|
|
||||||
fi
|
|
||||||
|
|
||||||
popd
|
|
||||||
|
|
||||||
echo "Successfully build overture"
|
echo "Successfully build overture"
|
||||||
|
|||||||
Submodule core/src/overture/src/github.com/shadowsocks/overture updated: 0743242de1...fae912215d
+2
-2
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId "com.github.shadowsocks"
|
applicationId "com.github.shadowsocks"
|
||||||
minSdkVersion rootProject.minSdkVersion
|
minSdkVersion rootProject.minSdkVersion
|
||||||
targetSdkVersion rootProject.sdkVersion
|
targetSdkVersion rootProject.sdkVersion
|
||||||
versionCode 202
|
versionCode 203
|
||||||
versionName "4.4.2"
|
versionName "4.4.3"
|
||||||
testApplicationId "com.github.shadowsocks.test"
|
testApplicationId "com.github.shadowsocks.test"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary true
|
vectorDrawables.useSupportLibrary true
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:allowBackup="true"
|
||||||
android:backupAgent=".ConfigBackupHelper"
|
android:backupAgent=".ConfigBackupHelper"
|
||||||
android:fullBackupContent="@xml/backup_descriptor"
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
android:fullBackupOnly="true"
|
android:fullBackupOnly="true"
|
||||||
|
|||||||
+3763
-3763
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+3763
-3763
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
package android.support.design.widget
|
||||||
|
|
||||||
|
import android.view.animation.Interpolator
|
||||||
|
|
||||||
|
object SnackbarAnimation {
|
||||||
|
val FAST_OUT_SLOW_IN_INTERPOLATOR: Interpolator get() = AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR
|
||||||
|
const val ANIMATION_DURATION = BaseTransientBottomBar.ANIMATION_DURATION.toLong()
|
||||||
|
}
|
||||||
@@ -61,7 +61,8 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
|
|||||||
private const val SELECTED_HOSTNAMES = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_HOSTNAMES"
|
private const val SELECTED_HOSTNAMES = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_HOSTNAMES"
|
||||||
private const val SELECTED_URLS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_URLS"
|
private const val SELECTED_URLS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_URLS"
|
||||||
|
|
||||||
private val PATTERN_DOMAIN = """(?<=^\(\^\|\\\.\)).*(?=\$$)""".toRegex()
|
// unescaped: (?<=^(\(\^\|\\\.\)|\^\(\.\*\\\.\)\?)).*(?=\$$)
|
||||||
|
private val PATTERN_DOMAIN = "(?<=^(\\(\\^\\|\\\\\\.\\)|\\^\\(\\.\\*\\\\\\.\\)\\?)).*(?=\\\$\$)".toRegex()
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class Template {
|
private enum class Template {
|
||||||
@@ -355,7 +356,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
|
|||||||
undoManager = UndoSnackbarManager(activity!!.findViewById(R.id.snackbar), adapter::undo)
|
undoManager = UndoSnackbarManager(activity!!.findViewById(R.id.snackbar), adapter::undo)
|
||||||
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START or ItemTouchHelper.END) {
|
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START or ItemTouchHelper.END) {
|
||||||
override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int =
|
override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int =
|
||||||
if (isEnabled) super.getSwipeDirs(recyclerView, viewHolder) else 0
|
if (isEnabled && selectedItems.isEmpty()) super.getSwipeDirs(recyclerView, viewHolder) else 0
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) =
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) =
|
||||||
adapter.remove(viewHolder.adapterPosition)
|
adapter.remove(viewHolder.adapterPosition)
|
||||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
|
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
|
||||||
@@ -438,4 +439,9 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
|
|||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDetach() {
|
||||||
|
undoManager.flush()
|
||||||
|
super.onDetach()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ import com.github.shadowsocks.utils.Action
|
|||||||
import com.github.shadowsocks.utils.broadcastReceiver
|
import com.github.shadowsocks.utils.broadcastReceiver
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Android < 8 VPN: always invisible because of VPN notification/icon
|
||||||
|
* Android 4.x other: always visible
|
||||||
|
* Android 5-7 other: only invisible in (possibly unsecure) lockscreen
|
||||||
|
* Android 8+: always visible due to system limitations
|
||||||
|
* (user can choose to hide the notification in secure lockscreen or anywhere)
|
||||||
|
*/
|
||||||
class ServiceNotification(private val service: BaseService.Interface, profileName: String,
|
class ServiceNotification(private val service: BaseService.Interface, profileName: String,
|
||||||
channel: String, private val visible: Boolean = false) {
|
channel: String, private val visible: Boolean = false) {
|
||||||
private val keyGuard = (service as Context).getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
private val keyGuard = (service as Context).getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||||
|
|||||||
@@ -20,8 +20,9 @@
|
|||||||
|
|
||||||
package com.github.shadowsocks.bg
|
package com.github.shadowsocks.bg
|
||||||
|
|
||||||
|
import android.app.KeyguardManager
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Build
|
|
||||||
import android.service.quicksettings.Tile
|
import android.service.quicksettings.Tile
|
||||||
import android.support.annotation.RequiresApi
|
import android.support.annotation.RequiresApi
|
||||||
import com.github.shadowsocks.App.Companion.app
|
import com.github.shadowsocks.App.Companion.app
|
||||||
@@ -31,34 +32,35 @@ import com.github.shadowsocks.aidl.IShadowsocksService
|
|||||||
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
|
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
|
||||||
import android.service.quicksettings.TileService as BaseTileService
|
import android.service.quicksettings.TileService as BaseTileService
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.N)
|
@RequiresApi(24)
|
||||||
class TileService : BaseTileService(), ShadowsocksConnection.Interface {
|
class TileService : BaseTileService(), ShadowsocksConnection.Interface {
|
||||||
private val iconIdle by lazy { Icon.createWithResource(this, R.drawable.ic_service_idle).setTint(0x79ffffff) }
|
private val iconIdle by lazy { Icon.createWithResource(this, R.drawable.ic_service_idle).setTint(0x79ffffff) }
|
||||||
private val iconBusy by lazy { Icon.createWithResource(this, R.drawable.ic_service_busy) }
|
private val iconBusy by lazy { Icon.createWithResource(this, R.drawable.ic_service_busy) }
|
||||||
private val iconConnected by lazy { Icon.createWithResource(this, R.drawable.ic_service_active) }
|
private val iconConnected by lazy { Icon.createWithResource(this, R.drawable.ic_service_active) }
|
||||||
|
private val keyguard by lazy { getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager }
|
||||||
|
|
||||||
override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy {
|
override val serviceCallback: IShadowsocksServiceCallback.Stub by lazy {
|
||||||
@RequiresApi(Build.VERSION_CODES.N)
|
@RequiresApi(24)
|
||||||
object : IShadowsocksServiceCallback.Stub() {
|
object : IShadowsocksServiceCallback.Stub() {
|
||||||
override fun stateChanged(state: Int, profileName: String?, msg: String?) {
|
override fun stateChanged(state: Int, profileName: String?, msg: String?) {
|
||||||
val tile = qsTile ?: return
|
val tile = qsTile ?: return
|
||||||
|
var label: String? = null
|
||||||
when (state) {
|
when (state) {
|
||||||
BaseService.STOPPED -> {
|
BaseService.STOPPED -> {
|
||||||
tile.icon = iconIdle
|
tile.icon = iconIdle
|
||||||
tile.label = getString(R.string.app_name)
|
|
||||||
tile.state = Tile.STATE_INACTIVE
|
tile.state = Tile.STATE_INACTIVE
|
||||||
}
|
}
|
||||||
BaseService.CONNECTED -> {
|
BaseService.CONNECTED -> {
|
||||||
tile.icon = iconConnected
|
tile.icon = iconConnected
|
||||||
tile.label = profileName ?: getString(R.string.app_name)
|
if (!keyguard.isDeviceLocked) label = profileName
|
||||||
tile.state = Tile.STATE_ACTIVE
|
tile.state = Tile.STATE_ACTIVE
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
tile.icon = iconBusy
|
tile.icon = iconBusy
|
||||||
tile.label = getString(R.string.app_name)
|
|
||||||
tile.state = Tile.STATE_UNAVAILABLE
|
tile.state = Tile.STATE_UNAVAILABLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tile.label = label ?: getString(R.string.app_name)
|
||||||
tile.updateTile()
|
tile.updateTile()
|
||||||
}
|
}
|
||||||
override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { }
|
override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { }
|
||||||
|
|||||||
+2
@@ -103,6 +103,8 @@ class BottomSheetPreferenceDialogFragment : PreferenceDialogFragmentCompat() {
|
|||||||
val activity = activity
|
val activity = activity
|
||||||
val dialog = BottomSheetDialog(activity!!, theme)
|
val dialog = BottomSheetDialog(activity!!, theme)
|
||||||
val recycler = RecyclerView(activity)
|
val recycler = RecyclerView(activity)
|
||||||
|
val padding = resources.getDimensionPixelOffset(R.dimen.bottom_sheet_padding)
|
||||||
|
recycler.setPadding(0, padding, 0, padding)
|
||||||
recycler.setHasFixedSize(true)
|
recycler.setHasFixedSize(true)
|
||||||
recycler.layoutManager = LinearLayoutManager(activity)
|
recycler.layoutManager = LinearLayoutManager(activity)
|
||||||
recycler.adapter = IconListAdapter(dialog)
|
recycler.adapter = IconListAdapter(dialog)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.support.annotation.AttrRes
|
|||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
import android.support.v4.app.FragmentManager
|
import android.support.v4.app.FragmentManager
|
||||||
import android.support.v7.util.SortedList
|
import android.support.v7.util.SortedList
|
||||||
|
import android.view.accessibility.AccessibilityManager
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import com.github.shadowsocks.App.Companion.app
|
import com.github.shadowsocks.App.Companion.app
|
||||||
import com.github.shadowsocks.JniHelper
|
import com.github.shadowsocks.JniHelper
|
||||||
@@ -21,6 +22,11 @@ private val fieldChildFragmentManager by lazy {
|
|||||||
field
|
field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isAccessibilityEnabled(context: Context): Boolean {
|
||||||
|
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
|
||||||
|
return am.isEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
fun String.isNumericAddress() = JniHelper.parseNumericAddress(this) != null
|
fun String.isNumericAddress() = JniHelper.parseNumericAddress(this) != null
|
||||||
fun String.parseNumericAddress(): InetAddress? {
|
fun String.parseNumericAddress(): InetAddress? {
|
||||||
val addr = JniHelper.parseNumericAddress(this)
|
val addr = JniHelper.parseNumericAddress(this)
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||||
|
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||||
|
* *
|
||||||
|
* 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/>. *
|
||||||
|
* *
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package com.github.shadowsocks.widget
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.design.widget.CoordinatorLayout
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import com.github.shadowsocks.R
|
||||||
|
|
||||||
|
class BottomMarginBehavior : CoordinatorLayout.Behavior<View> {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||||
|
|
||||||
|
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
|
||||||
|
dependency.id == R.id.stat // sorry I'm too lazy to write an attribute
|
||||||
|
|
||||||
|
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||||
|
(child.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = (parent.height - dependency.y).toInt()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||||
|
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||||
|
* *
|
||||||
|
* 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/>. *
|
||||||
|
* *
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package com.github.shadowsocks.widget
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.support.design.widget.CoordinatorLayout
|
||||||
|
import android.support.design.widget.SnackbarAnimation
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.support.v4.view.ViewCompat
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
import com.github.shadowsocks.utils.isAccessibilityEnabled
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full credits go to: https://stackoverflow.com/a/35904421/2245107
|
||||||
|
*/
|
||||||
|
class MoveUpwardBehavior : CoordinatorLayout.Behavior<View> {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||||
|
|
||||||
|
override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean =
|
||||||
|
dependency is Snackbar.SnackbarLayout
|
||||||
|
|
||||||
|
override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
|
||||||
|
child.translationY = Math.min(0f, dependency.translationY - dependency.height)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on BaseTransientBottomBar.animateViewOut (support lib 27.0.2).
|
||||||
|
*/
|
||||||
|
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
|
||||||
|
if (!isAccessibilityEnabled(parent.getContext())) {
|
||||||
|
val animator = ValueAnimator()
|
||||||
|
val start = child.translationY
|
||||||
|
animator.setFloatValues(start, 0f)
|
||||||
|
animator.interpolator = SnackbarAnimation.FAST_OUT_SLOW_IN_INTERPOLATOR
|
||||||
|
animator.duration = SnackbarAnimation.ANIMATION_DURATION
|
||||||
|
animator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
|
||||||
|
private var previousValue = start
|
||||||
|
|
||||||
|
override fun onAnimationUpdate(animator: ValueAnimator) {
|
||||||
|
val currentValue = animator.animatedValue as Float
|
||||||
|
child.translationY = currentValue
|
||||||
|
previousValue = currentValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
animator.start()
|
||||||
|
} else {
|
||||||
|
child.translationY = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,6 +108,7 @@ class ServiceButton @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
animationQueue.peekFirst()?.stop()
|
animationQueue.peekFirst()?.stop()
|
||||||
animationQueue.clear()
|
animationQueue.clear()
|
||||||
setImageDrawable(icon)
|
setImageDrawable(icon)
|
||||||
|
icon.start() // force ensureAnimatorSet to be called so that stop() will work
|
||||||
icon.stop()
|
icon.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +1,139 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.design.widget.CoordinatorLayout
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent" android:layout_height="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
<FrameLayout android:id="@+id/fragment_holder"
|
android:id="@+id/snackbar"
|
||||||
android:layout_width="match_parent"
|
android:background="@color/material_primary_700"
|
||||||
android:layout_height="match_parent"
|
android:clipChildren="false"
|
||||||
android:layout_above="@+id/stat"/>
|
android:layout_width="match_parent"
|
||||||
<LinearLayout android:id="@+id/stat"
|
android:layout_height="match_parent">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
<FrameLayout
|
||||||
android:elevation="4dp"
|
android:id="@+id/fragment_holder"
|
||||||
android:paddingStart="16dip"
|
android:background="?android:attr/windowBackground"
|
||||||
android:paddingEnd="16dip"
|
android:layout_width="match_parent"
|
||||||
android:paddingTop="16dip"
|
android:layout_height="match_parent" />
|
||||||
android:paddingBottom="16dip"
|
|
||||||
android:focusable="true"
|
<LinearLayout
|
||||||
android:background="@drawable/background_stat"
|
android:id="@+id/stat"
|
||||||
android:orientation="vertical"
|
android:layout_width="match_parent"
|
||||||
android:nextFocusRight="@+id/fab"
|
android:layout_height="wrap_content"
|
||||||
android:layout_above="@+id/snackbar">
|
android:background="@drawable/background_stat"
|
||||||
<TextView android:id="@+id/status"
|
android:elevation="4dp"
|
||||||
android:textSize="16sp"
|
android:focusable="true"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:nextFocusRight="@+id/fab"
|
||||||
android:ellipsize="marquee"
|
android:orientation="vertical"
|
||||||
android:paddingEnd="72dip"
|
android:paddingBottom="16dip"
|
||||||
android:layout_width="wrap_content"
|
android:paddingEnd="16dip"
|
||||||
android:layout_height="wrap_content"
|
android:paddingStart="16dip"
|
||||||
tools:ignore="RtlSymmetry"/>
|
android:paddingTop="16dip"
|
||||||
<com.github.shadowsocks.widget.BoundedGridLayout android:layout_width="match_parent"
|
android:layout_gravity="bottom"
|
||||||
android:layout_height="wrap_content"
|
app:layout_behavior="com.github.shadowsocks.widget.MoveUpwardBehavior">
|
||||||
app:bounded_width="288dp"
|
|
||||||
app:columnCount="4">
|
<TextView
|
||||||
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
|
android:id="@+id/status"
|
||||||
android:ellipsize="marquee" android:text="@string/sent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="24dp"
|
android:ellipsize="marquee"
|
||||||
android:gravity="end"/>
|
android:paddingEnd="72dip"
|
||||||
<TextView app:layout_column="1" android:id="@+id/tx" android:textSize="14sp"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
android:textSize="16sp"
|
||||||
android:layout_width="0dp" android:layout_height="wrap_content"
|
tools:ignore="RtlSymmetry"/>
|
||||||
app:layout_columnWeight="1" android:layout_gravity="fill_horizontal"/>
|
|
||||||
<TextView app:layout_column="2" android:textSize="14sp"
|
<com.github.shadowsocks.widget.BoundedGridLayout
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:layout_width="match_parent"
|
||||||
android:ellipsize="marquee" android:text="▲"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
app:bounded_width="288dp"
|
||||||
<TextView app:layout_column="3" android:id="@+id/txRate" android:textSize="14sp"
|
app:columnCount="4">
|
||||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
|
||||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
<TextView
|
||||||
android:layout_gravity="fill_horizontal" android:gravity="end"/>
|
style="@style/TextAppearance.AppCompat.Body2"
|
||||||
<TextView app:layout_row="1" app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="24dp"
|
android:layout_marginEnd="24dp"
|
||||||
android:gravity="end"
|
android:ellipsize="marquee"
|
||||||
android:ellipsize="marquee" android:text="@string/received"/>
|
android:gravity="end"
|
||||||
<TextView app:layout_row="1" app:layout_column="1" android:id="@+id/rx" android:textSize="14sp"
|
android:text="@string/sent"
|
||||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
app:layout_column="0"/>
|
||||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
|
||||||
android:layout_gravity="fill_horizontal" />
|
<TextView
|
||||||
<TextView app:layout_row="1" app:layout_column="2" android:textSize="14sp"
|
android:id="@+id/tx"
|
||||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
android:layout_width="0dp"
|
||||||
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="▼"/>
|
android:layout_height="wrap_content"
|
||||||
<TextView app:layout_row="1" app:layout_column="3" android:id="@+id/rxRate" android:textSize="14sp"
|
android:layout_gravity="fill_horizontal"
|
||||||
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:layout_gravity="fill_horizontal" android:gravity="end"/>
|
android:textSize="14sp"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:text="▲"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_column="2"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txRate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="fill_horizontal"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_column="3"
|
||||||
|
app:layout_columnWeight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/received"
|
||||||
|
app:layout_column="0"
|
||||||
|
app:layout_row="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rx"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="fill_horizontal"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_column="1"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_row="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:text="▼"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_column="2"
|
||||||
|
app:layout_row="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rxRate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="fill_horizontal"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_column="3"
|
||||||
|
app:layout_columnWeight="1"
|
||||||
|
app:layout_row="1"/>
|
||||||
</com.github.shadowsocks.widget.BoundedGridLayout>
|
</com.github.shadowsocks.widget.BoundedGridLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -72,11 +141,11 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignTop="@+id/stat"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginTop="@dimen/fab_margin_top"
|
android:layout_marginTop="@dimen/fab_margin_top"
|
||||||
android:elevation="6dp">
|
android:elevation="6dp"
|
||||||
|
app:layout_anchor="@id/stat"
|
||||||
|
app:layout_anchorGravity="top|end">
|
||||||
<com.github.shadowsocks.widget.ServiceButton
|
<com.github.shadowsocks.widget.ServiceButton
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -86,9 +155,4 @@
|
|||||||
app:pressedTranslationZ="6dp"
|
app:pressedTranslationZ="6dp"
|
||||||
app:srcCompat="@drawable/ic_service_idle"/>
|
app:srcCompat="@drawable/ic_service_idle"/>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
<android.support.design.widget.CoordinatorLayout
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
android:id="@+id/snackbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentBottom="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|||||||
@@ -3,5 +3,6 @@
|
|||||||
<dimen name="profile_item_max_width">480dp</dimen>
|
<dimen name="profile_item_max_width">480dp</dimen>
|
||||||
<dimen name="qr_code_size">250dp</dimen>
|
<dimen name="qr_code_size">250dp</dimen>
|
||||||
<dimen name="profile_padding">8dp</dimen>
|
<dimen name="profile_padding">8dp</dimen>
|
||||||
|
<dimen name="bottom_sheet_padding">8dp</dimen>
|
||||||
<dimen name="fab_margin_top">-44dp</dimen>
|
<dimen name="fab_margin_top">-44dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
apply from: 'gradle-mvn-push.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
buildToolsVersion rootProject.buildToolsVersion
|
buildToolsVersion rootProject.buildToolsVersion
|
||||||
compileSdkVersion rootProject.sdkVersion
|
compileSdkVersion rootProject.sdkVersion
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013 Chris Banes
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'maven'
|
||||||
|
apply plugin: 'signing'
|
||||||
|
|
||||||
|
@SuppressWarnings(["GroovyUnusedDeclaration", "GrMethodMayBeStatic"])
|
||||||
|
def isReleaseBuild() {
|
||||||
|
return !android.defaultConfig.versionName.contains("SNAPSHOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GroovyUnusedDeclaration")
|
||||||
|
def getReleaseRepositoryUrl() {
|
||||||
|
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
|
||||||
|
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GroovyUnusedDeclaration")
|
||||||
|
def getSnapshotRepositoryUrl() {
|
||||||
|
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
|
||||||
|
: "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GroovyUnusedDeclaration")
|
||||||
|
def getRepositoryUsername() {
|
||||||
|
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("GroovyUnusedDeclaration")
|
||||||
|
def getRepositoryPassword() {
|
||||||
|
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate { project ->
|
||||||
|
uploadArchives {
|
||||||
|
repositories {
|
||||||
|
mavenDeployer {
|
||||||
|
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||||
|
|
||||||
|
pom.groupId = 'com.github.shadowsocks'
|
||||||
|
pom.artifactId = 'plugin'
|
||||||
|
pom.version = android.defaultConfig.versionName
|
||||||
|
|
||||||
|
repository(url: getReleaseRepositoryUrl()) {
|
||||||
|
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||||
|
}
|
||||||
|
snapshotRepository(url: getSnapshotRepositoryUrl()) {
|
||||||
|
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||||
|
}
|
||||||
|
|
||||||
|
pom.project {
|
||||||
|
name 'Shadowsocks Plugin'
|
||||||
|
packaging 'aar'
|
||||||
|
description 'Shadowsocks Plugin'
|
||||||
|
url 'https://github.com/shadowsocks/shadowsocks-android'
|
||||||
|
|
||||||
|
scm {
|
||||||
|
url 'https://github.com/shadowsocks/shadowsocks-android.git'
|
||||||
|
connection 'scm:git:git://github.com/shadowsocks/shadowsocks-android.git'
|
||||||
|
developerConnection 'scm:git:git@github.com:shadowsocks/shadowsocks-android.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name 'GPLv3'
|
||||||
|
url 'https://www.gnu.org/licenses/gpl-3.0.html'
|
||||||
|
distribution 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id 'madeye'
|
||||||
|
name 'Max Lv'
|
||||||
|
url 'https://github.com/madeye'
|
||||||
|
}
|
||||||
|
developer {
|
||||||
|
id 'Mygod'
|
||||||
|
name 'Mygod Studio'
|
||||||
|
url 'https://github.com/Mygod'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signing {
|
||||||
|
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
|
||||||
|
sign configurations.archives
|
||||||
|
}
|
||||||
|
|
||||||
|
android.libraryVariants.all { variant ->
|
||||||
|
def javadocTask = task("generate${variant.name.capitalize()}Javadoc", type: Javadoc) {
|
||||||
|
description "Generates Javadoc for $variant.name."
|
||||||
|
source = variant.javaCompiler.source
|
||||||
|
ext.androidJar = project.files(android.getBootClasspath().join(File.pathSeparator))
|
||||||
|
classpath = files(variant.javaCompiler.classpath.files) + files(ext.androidJar)
|
||||||
|
exclude '**/BuildConfig.java'
|
||||||
|
exclude '**/R.java'
|
||||||
|
}
|
||||||
|
|
||||||
|
javadocTask.dependsOn variant.javaCompiler
|
||||||
|
|
||||||
|
def jarJavadocTask = task("jar${variant.name.capitalize()}Javadoc", type: Jar) {
|
||||||
|
description "Generate Javadoc Jar for $variant.name"
|
||||||
|
classifier = 'javadoc'
|
||||||
|
from javadocTask.destinationDir
|
||||||
|
}
|
||||||
|
|
||||||
|
jarJavadocTask.dependsOn javadocTask
|
||||||
|
artifacts.add('archives', jarJavadocTask)
|
||||||
|
|
||||||
|
def jarSourceTask = task("jar${variant.name.capitalize()}Sources", type: Jar) {
|
||||||
|
description "Generates Java Sources for $variant.name."
|
||||||
|
classifier = 'sources'
|
||||||
|
from variant.javaCompiler.source
|
||||||
|
}
|
||||||
|
|
||||||
|
jarSourceTask.dependsOn variant.javaCompiler
|
||||||
|
artifacts.add('archives', jarSourceTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user