Compare commits

...
16 Commits
Author SHA1 Message Date
Max Lv a40f87f492 Fix a rounding issue on Android 4.4
We can use setTranslationY directly on Android 4.4. Otherwise, we need
to handle float to int routing issue very carefully.
2017-12-30 15:04:04 +08:00
Max Lv 56f4a36a18 Fix an issue when Accessibility enabled
Actually, it's not necessary to move our fragment upward, which contains
a list view.
2017-12-30 14:45:05 +08:00
Max Lv 6db818cc46 Update shadowsocks-libev 2017-12-30 11:25:12 +08:00
Mygod c86b68c0e6 Add missing flush call in CustomRulesFragment 2017-12-29 22:55:08 +08:00
Mygod 56728a3f40 Hide profileName in secure lock screen 2017-12-29 22:43:01 +08:00
Mygod cc070223b4 Add doc for ServiceNotification 2017-12-29 22:32:07 +08:00
Mygod 0335d67dba Add animation for bottom bar during Snackbar transitions
Fix #1036 the one-year-old issue.

There are still some subtle blackness during the exit animation. Unfortunately this is something I am not able to fix as I don't and probably won't ever have enough knowledge about Android's drawing passes. Anyway this still looks much better than before.
2017-12-29 20:58:02 +08:00
Mygod 0d0c2d59de Add android:allowBackup to suppress warning 2017-12-29 18:32:49 +08:00
Max Lv 16ee0edea8 Bump version 2017-12-27 00:55:33 -08:00
Mygod e03c34aa19 Refine bottom sheet paddings
Matches design in Google Authenticator app.
2017-12-27 12:28:21 +08:00
Mygod db011afcc0 Fix connected button not rendered correctly 2017-12-27 12:11:32 +08:00
Mygod ebe4d6fd0c Disable swipe to remove in selection mode 2017-12-27 11:47:46 +08:00
Mygod cb9356e5e0 Refine acls 2017-12-27 11:43:17 +08:00
Mygod c55fd2dd65 Refine goBuild speed 2017-12-27 11:23:38 +08:00
Mygod 3c3406d3c7 Update dependencies 2017-12-27 11:23:38 +08:00
Max Lv e603700587 Update shadowsocks-libev and overture 2017-12-25 20:19:52 -08:00
23 changed files with 11761 additions and 11404 deletions
+1 -1
View File
@@ -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
View File
@@ -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'
+30 -25
View File
@@ -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"
+2 -2
View File
@@ -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
+1
View File
@@ -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"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
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) { }
@@ -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()
} }
} }
+139 -75
View File
@@ -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>
+1
View File
@@ -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>
+2
View File
@@ -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
+136
View File
@@ -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)
}
}