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
|
||||
- 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
@@ -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'
|
||||
|
||||
Submodule core/src/main/jni/shadowsocks-libev updated: 0c3cf8beb3...cba94db423
+30
-25
@@ -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"
|
||||
|
||||
Submodule core/src/overture/src/github.com/shadowsocks/overture updated: 0743242de1...fae912215d
+2
-2
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
+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_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) { }
|
||||
|
||||
+2
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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