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
- touch $HOME/.android/repositories.cfg
- yes | sdkmanager
"build-tools;27.0.2"
"build-tools;27.0.3"
ndk-bundle
"platforms;android-27"
>/dev/null
+2 -2
View File
@@ -5,10 +5,10 @@ buildscript {
kotlinVersion = '1.2.10'
minSdkVersion = 19
sdkVersion = 27
buildToolsVersion = '27.0.2'
buildToolsVersion = '27.0.3'
supportLibraryVersion = '27.0.2'
takisoftFixVersion = '27.0.2.0'
playServicesVersion = '11.6.2'
playServicesVersion = '11.8.0'
junitVersion = '4.12'
androidTestVersion = '1.0.1'
androidEspressoVersion = '3.0.1'
+30 -25
View File
@@ -56,33 +56,38 @@ export GOROOT=$DIR/go
export GOPATH=$DIR
export PATH=$GOROOT/bin:$GOPATH/bin:$PATH
echo "Get dependences for overture"
go get -u github.com/tools/godep
if [ ! -f "$TARGET/armeabi-v7a/liboverture.so" ] || [ ! -f "$TARGET/arm64-v8a/liboverture.so" ] ||
[ ! -f "$TARGET/x86/liboverture.so" ]; then
pushd $GOPATH/src/github.com/shadowsocks/overture/main
godep restore
echo "Get dependences for overture"
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
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"
+2 -2
View File
@@ -20,8 +20,8 @@ android {
applicationId "com.github.shadowsocks"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion
versionCode 202
versionName "4.4.2"
versionCode 203
versionName "4.4.3"
testApplicationId "com.github.shadowsocks.test"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary true
+1
View File
@@ -20,6 +20,7 @@
<application
android:name=".App"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:backupAgent=".ConfigBackupHelper"
android:fullBackupContent="@xml/backup_descriptor"
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_URLS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_URLS"
private val PATTERN_DOMAIN = """(?<=^\(\^\|\\\.\)).*(?=\$$)""".toRegex()
// unescaped: (?<=^(\(\^\|\\\.\)|\^\(\.\*\\\.\)\?)).*(?=\$$)
private val PATTERN_DOMAIN = "(?<=^(\\(\\^\\|\\\\\\.\\)|\\^\\(\\.\\*\\\\\\.\\)\\?)).*(?=\\\$\$)".toRegex()
}
private enum class Template {
@@ -355,7 +356,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
undoManager = UndoSnackbarManager(activity!!.findViewById(R.id.snackbar), adapter::undo)
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START or ItemTouchHelper.END) {
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) =
adapter.remove(viewHolder.adapterPosition)
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
@@ -438,4 +439,9 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
}
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 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,
channel: String, private val visible: Boolean = false) {
private val keyGuard = (service as Context).getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
@@ -20,8 +20,9 @@
package com.github.shadowsocks.bg
import android.app.KeyguardManager
import android.content.Context
import android.graphics.drawable.Icon
import android.os.Build
import android.service.quicksettings.Tile
import android.support.annotation.RequiresApi
import com.github.shadowsocks.App.Companion.app
@@ -31,34 +32,35 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import android.service.quicksettings.TileService as BaseTileService
@RequiresApi(Build.VERSION_CODES.N)
@RequiresApi(24)
class TileService : BaseTileService(), ShadowsocksConnection.Interface {
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 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 {
@RequiresApi(Build.VERSION_CODES.N)
@RequiresApi(24)
object : IShadowsocksServiceCallback.Stub() {
override fun stateChanged(state: Int, profileName: String?, msg: String?) {
val tile = qsTile ?: return
var label: String? = null
when (state) {
BaseService.STOPPED -> {
tile.icon = iconIdle
tile.label = getString(R.string.app_name)
tile.state = Tile.STATE_INACTIVE
}
BaseService.CONNECTED -> {
tile.icon = iconConnected
tile.label = profileName ?: getString(R.string.app_name)
if (!keyguard.isDeviceLocked) label = profileName
tile.state = Tile.STATE_ACTIVE
}
else -> {
tile.icon = iconBusy
tile.label = getString(R.string.app_name)
tile.state = Tile.STATE_UNAVAILABLE
}
}
tile.label = label ?: getString(R.string.app_name)
tile.updateTile()
}
override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { }
@@ -103,6 +103,8 @@ class BottomSheetPreferenceDialogFragment : PreferenceDialogFragmentCompat() {
val activity = activity
val dialog = BottomSheetDialog(activity!!, theme)
val recycler = RecyclerView(activity)
val padding = resources.getDimensionPixelOffset(R.dimen.bottom_sheet_padding)
recycler.setPadding(0, padding, 0, padding)
recycler.setHasFixedSize(true)
recycler.layoutManager = LinearLayoutManager(activity)
recycler.adapter = IconListAdapter(dialog)
@@ -9,6 +9,7 @@ import android.support.annotation.AttrRes
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v7.util.SortedList
import android.view.accessibility.AccessibilityManager
import android.util.TypedValue
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.JniHelper
@@ -21,6 +22,11 @@ private val fieldChildFragmentManager by lazy {
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.parseNumericAddress(): InetAddress? {
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.clear()
setImageDrawable(icon)
icon.start() // force ensureAnimatorSet to be called so that stop() will work
icon.stop()
}
}
+139 -75
View File
@@ -1,70 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
<FrameLayout android:id="@+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/stat"/>
<LinearLayout android:id="@+id/stat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:paddingTop="16dip"
android:paddingBottom="16dip"
android:focusable="true"
android:background="@drawable/background_stat"
android:orientation="vertical"
android:nextFocusRight="@+id/fab"
android:layout_above="@+id/snackbar">
<TextView android:id="@+id/status"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:paddingEnd="72dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="RtlSymmetry"/>
<com.github.shadowsocks.widget.BoundedGridLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bounded_width="288dp"
app:columnCount="4">
<TextView app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:ellipsize="marquee" android:text="@string/sent"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:gravity="end"/>
<TextView app:layout_column="1" android:id="@+id/tx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content"
app:layout_columnWeight="1" android:layout_gravity="fill_horizontal"/>
<TextView app:layout_column="2" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee" android:text="▲"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView app:layout_column="3" android:id="@+id/txRate" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" android:gravity="end"/>
<TextView app:layout_row="1" app:layout_column="0" style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:gravity="end"
android:ellipsize="marquee" android:text="@string/received"/>
<TextView app:layout_row="1" app:layout_column="1" android:id="@+id/rx" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" />
<TextView app:layout_row="1" app:layout_column="2" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="▼"/>
<TextView app:layout_row="1" app:layout_column="3" android:id="@+id/rxRate" android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary" android:ellipsize="marquee"
android:layout_width="0dp" android:layout_height="wrap_content" app:layout_columnWeight="1"
android:layout_gravity="fill_horizontal" android:gravity="end"/>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/snackbar"
android:background="@color/material_primary_700"
android:clipChildren="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/fragment_holder"
android:background="?android:attr/windowBackground"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/stat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_stat"
android:elevation="4dp"
android:focusable="true"
android:nextFocusRight="@+id/fab"
android:orientation="vertical"
android:paddingBottom="16dip"
android:paddingEnd="16dip"
android:paddingStart="16dip"
android:paddingTop="16dip"
android:layout_gravity="bottom"
app:layout_behavior="com.github.shadowsocks.widget.MoveUpwardBehavior">
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:paddingEnd="72dip"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:ignore="RtlSymmetry"/>
<com.github.shadowsocks.widget.BoundedGridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bounded_width="288dp"
app:columnCount="4">
<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/sent"
app:layout_column="0"/>
<TextView
android:id="@+id/tx"
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"/>
<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>
</LinearLayout>
@@ -72,11 +141,11 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/stat"
android:layout_marginEnd="16dp"
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
android:id="@+id/fab"
android:layout_width="wrap_content"
@@ -86,9 +155,4 @@
app:pressedTranslationZ="6dp"
app:srcCompat="@drawable/ic_service_idle"/>
</FrameLayout>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/snackbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
+1
View File
@@ -3,5 +3,6 @@
<dimen name="profile_item_max_width">480dp</dimen>
<dimen name="qr_code_size">250dp</dimen>
<dimen name="profile_padding">8dp</dimen>
<dimen name="bottom_sheet_padding">8dp</dimen>
<dimen name="fab_margin_top">-44dp</dimen>
</resources>
+2
View File
@@ -1,6 +1,8 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply from: 'gradle-mvn-push.gradle'
android {
buildToolsVersion rootProject.buildToolsVersion
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)
}
}