Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
4126b313
Unverified
Commit
4126b313
authored
Jan 12, 2020
by
Max Lv
Committed by
GitHub
Jan 12, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2402 from Mygod/subscription
Refactor subscriptions
parents
0154cfda
56e788a8
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
249 additions
and
128 deletions
+249
-128
core/src/main/AndroidManifest.xml
core/src/main/AndroidManifest.xml
+5
-0
core/src/main/java/com/github/shadowsocks/Core.kt
core/src/main/java/com/github/shadowsocks/Core.kt
+6
-4
core/src/main/java/com/github/shadowsocks/database/Profile.kt
.../src/main/java/com/github/shadowsocks/database/Profile.kt
+7
-2
core/src/main/java/com/github/shadowsocks/database/ProfileManager.kt
...in/java/com/github/shadowsocks/database/ProfileManager.kt
+12
-34
core/src/main/java/com/github/shadowsocks/subscription/SubscriptionService.kt
...om/github/shadowsocks/subscription/SubscriptionService.kt
+184
-0
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
+12
-2
core/src/main/res/drawable/ic_file_cloud_download.xml
core/src/main/res/drawable/ic_file_cloud_download.xml
+0
-0
core/src/main/res/values/strings.xml
core/src/main/res/values/strings.xml
+3
-0
mobile/build.gradle
mobile/build.gradle
+0
-1
mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
...main/java/com/github/shadowsocks/ProfileConfigFragment.kt
+3
-3
mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt
.../src/main/java/com/github/shadowsocks/ProfilesFragment.kt
+2
-2
mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt
...java/com/github/shadowsocks/UdpFallbackProfileActivity.kt
+1
-1
mobile/src/main/java/com/github/shadowsocks/subscription/SubscriptionFragment.kt
...m/github/shadowsocks/subscription/SubscriptionFragment.kt
+8
-60
mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
...main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
+1
-1
mobile/src/main/res/drawable/ic_file_cloud_queue.xml
mobile/src/main/res/drawable/ic_file_cloud_queue.xml
+0
-0
mobile/src/main/res/layout/layout_profile.xml
mobile/src/main/res/layout/layout_profile.xml
+1
-1
mobile/src/main/res/layout/layout_subscriptions.xml
mobile/src/main/res/layout/layout_subscriptions.xml
+0
-13
mobile/src/main/res/menu/navigation_main.xml
mobile/src/main/res/menu/navigation_main.xml
+1
-1
mobile/src/main/res/menu/subscription_menu.xml
mobile/src/main/res/menu/subscription_menu.xml
+1
-1
tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt
.../java/com/github/shadowsocks/tv/MainPreferenceFragment.kt
+1
-1
tv/src/main/java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt
.../java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt
+1
-1
No files found.
core/src/main/AndroidManifest.xml
View file @
4126b313
...
@@ -61,6 +61,11 @@
...
@@ -61,6 +61,11 @@
android:exported=
"false"
>
android:exported=
"false"
>
</service>
</service>
<service
android:name=
"com.github.shadowsocks.subscription.SubscriptionService"
android:exported=
"false"
>
</service>
<activity
<activity
android:name=
"com.github.shadowsocks.UrlImportActivity"
android:name=
"com.github.shadowsocks.UrlImportActivity"
android:theme=
"@style/Theme.AppCompat.Translucent"
android:theme=
"@style/Theme.AppCompat.Translucent"
...
...
core/src/main/java/com/github/shadowsocks/Core.kt
View file @
4126b313
...
@@ -42,6 +42,7 @@ import com.github.shadowsocks.database.Profile
...
@@ -42,6 +42,7 @@ import com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.net.TcpFastOpen
import
com.github.shadowsocks.net.TcpFastOpen
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.subscription.SubscriptionService
import
com.github.shadowsocks.utils.*
import
com.github.shadowsocks.utils.*
import
com.google.firebase.FirebaseApp
import
com.google.firebase.FirebaseApp
import
com.google.firebase.analytics.FirebaseAnalytics
import
com.google.firebase.analytics.FirebaseAnalytics
...
@@ -61,6 +62,7 @@ object Core {
...
@@ -61,6 +62,7 @@ object Core {
lateinit
var
configureIntent
:
(
Context
)
->
PendingIntent
lateinit
var
configureIntent
:
(
Context
)
->
PendingIntent
val
activity
by
lazy
{
app
.
getSystemService
<
ActivityManager
>()
!!
}
val
activity
by
lazy
{
app
.
getSystemService
<
ActivityManager
>()
!!
}
val
connectivity
by
lazy
{
app
.
getSystemService
<
ConnectivityManager
>()
!!
}
val
connectivity
by
lazy
{
app
.
getSystemService
<
ConnectivityManager
>()
!!
}
val
notification
by
lazy
{
app
.
getSystemService
<
NotificationManager
>()
!!
}
val
packageInfo
:
PackageInfo
by
lazy
{
getPackageInfo
(
app
.
packageName
)
}
val
packageInfo
:
PackageInfo
by
lazy
{
getPackageInfo
(
app
.
packageName
)
}
val
deviceStorage
by
lazy
{
if
(
Build
.
VERSION
.
SDK_INT
<
24
)
app
else
DeviceStorageApp
(
app
)
}
val
deviceStorage
by
lazy
{
if
(
Build
.
VERSION
.
SDK_INT
<
24
)
app
else
DeviceStorageApp
(
app
)
}
val
analytics
:
FirebaseAnalytics
by
lazy
{
FirebaseAnalytics
.
getInstance
(
deviceStorage
)
}
val
analytics
:
FirebaseAnalytics
by
lazy
{
FirebaseAnalytics
.
getInstance
(
deviceStorage
)
}
...
@@ -128,16 +130,16 @@ object Core {
...
@@ -128,16 +130,16 @@ object Core {
fun
updateNotificationChannels
()
{
fun
updateNotificationChannels
()
{
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
@RequiresApi
(
26
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
@RequiresApi
(
26
)
{
val
nm
=
app
.
getSystemService
<
NotificationManager
>()
!!
notification
.
createNotificationChannels
(
listOf
(
nm
.
createNotificationChannels
(
listOf
(
NotificationChannel
(
"service-vpn"
,
app
.
getText
(
R
.
string
.
service_vpn
),
NotificationChannel
(
"service-vpn"
,
app
.
getText
(
R
.
string
.
service_vpn
),
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
NotificationManager
.
IMPORTANCE_MIN
if
(
Build
.
VERSION
.
SDK_INT
>=
28
)
NotificationManager
.
IMPORTANCE_MIN
else
NotificationManager
.
IMPORTANCE_LOW
),
// #1355
else
NotificationManager
.
IMPORTANCE_LOW
),
// #1355
NotificationChannel
(
"service-proxy"
,
app
.
getText
(
R
.
string
.
service_proxy
),
NotificationChannel
(
"service-proxy"
,
app
.
getText
(
R
.
string
.
service_proxy
),
NotificationManager
.
IMPORTANCE_LOW
),
NotificationManager
.
IMPORTANCE_LOW
),
NotificationChannel
(
"service-transproxy"
,
app
.
getText
(
R
.
string
.
service_transproxy
),
NotificationChannel
(
"service-transproxy"
,
app
.
getText
(
R
.
string
.
service_transproxy
),
NotificationManager
.
IMPORTANCE_LOW
)))
NotificationManager
.
IMPORTANCE_LOW
),
nm
.
deleteNotificationChannel
(
"service-nat"
)
// NAT mode is gone for good
SubscriptionService
.
notificationChannel
))
notification
.
deleteNotificationChannel
(
"service-nat"
)
// NAT mode is gone for good
}
}
}
}
...
...
core/src/main/java/com/github/shadowsocks/database/Profile.kt
View file @
4126b313
...
@@ -53,10 +53,12 @@ data class Profile(
...
@@ -53,10 +53,12 @@ data class Profile(
// user configurable fields
// user configurable fields
var
name
:
String
?
=
""
,
var
name
:
String
?
=
""
,
var
host
:
String
=
sponsored
,
var
host
:
String
=
sponsored
,
var
remotePort
:
Int
=
8388
,
var
remotePort
:
Int
=
8388
,
var
password
:
String
=
"u1rRWTssNv0p"
,
var
password
:
String
=
"u1rRWTssNv0p"
,
var
method
:
String
=
"aes-256-cfb"
,
var
method
:
String
=
"aes-256-cfb"
,
var
route
:
String
=
"all"
,
var
route
:
String
=
"all"
,
var
remoteDns
:
String
=
"dns.google"
,
var
remoteDns
:
String
=
"dns.google"
,
var
proxyApps
:
Boolean
=
false
,
var
proxyApps
:
Boolean
=
false
,
...
@@ -252,8 +254,11 @@ data class Profile(
...
@@ -252,8 +254,11 @@ data class Profile(
@Query
(
"SELECT * FROM `Profile` WHERE `id` = :id"
)
@Query
(
"SELECT * FROM `Profile` WHERE `id` = :id"
)
operator
fun
get
(
id
:
Long
):
Profile
?
operator
fun
get
(
id
:
Long
):
Profile
?
@Query
(
"SELECT * FROM `Profile` ORDER BY `userOrder`"
)
@Query
(
"SELECT * FROM `Profile` WHERE `Subscription` != 2 ORDER BY `userOrder`"
)
fun
list
():
List
<
Profile
>
fun
listActive
():
List
<
Profile
>
@Query
(
"SELECT * FROM `Profile`"
)
fun
listAll
():
List
<
Profile
>
@Query
(
"SELECT MAX(`userOrder`) + 1 FROM `Profile`"
)
@Query
(
"SELECT MAX(`userOrder`) + 1 FROM `Profile`"
)
fun
nextOrder
():
Long
?
fun
nextOrder
():
Long
?
...
...
core/src/main/java/com/github/shadowsocks/database/ProfileManager.kt
View file @
4126b313
...
@@ -54,38 +54,6 @@ object ProfileManager {
...
@@ -54,38 +54,6 @@ object ProfileManager {
return
profile
return
profile
}
}
fun
createProfilesFromSubscription
(
jsons
:
Sequence
<
InputStream
>,
replace
:
Boolean
,
oldProfiles
:
List
<
Profile
>?)
{
val
profiles
=
oldProfiles
?.
associateBy
{
it
.
formattedAddress
}
val
feature
=
profiles
?.
values
?.
singleOrNull
{
it
.
id
==
DataStore
.
profileId
}
val
lazyClear
=
lazy
{
clear
()
}
jsons
.
asIterable
().
forEachTry
{
json
->
Profile
.
parseJson
(
JsonStreamParser
(
json
.
bufferedReader
()).
asSequence
().
single
(),
feature
)
{
if
(
replace
)
{
lazyClear
.
value
}
// if two profiles has the same address, treat them as the same profile and copy settings over
profiles
?.
get
(
it
.
formattedAddress
)
?.
apply
{
it
.
tx
=
tx
it
.
rx
=
rx
it
.
individual
=
individual
it
.
route
=
route
it
.
bypass
=
bypass
it
.
ipv6
=
ipv6
it
.
metered
=
metered
it
.
proxyApps
=
proxyApps
it
.
remoteDns
=
remoteDns
it
.
udpdns
=
udpdns
it
.
udpFallback
=
udpFallback
}
it
.
subscription
=
Profile
.
SubscriptionStatus
.
Active
createProfile
(
it
)
}
}
}
fun
createProfilesFromJson
(
jsons
:
Sequence
<
InputStream
>,
replace
:
Boolean
=
false
)
{
fun
createProfilesFromJson
(
jsons
:
Sequence
<
InputStream
>,
replace
:
Boolean
=
false
)
{
val
profiles
=
if
(
replace
)
getAllProfiles
()
?.
associateBy
{
it
.
formattedAddress
}
else
null
val
profiles
=
if
(
replace
)
getAllProfiles
()
?.
associateBy
{
it
.
formattedAddress
}
else
null
val
feature
=
if
(
replace
)
{
val
feature
=
if
(
replace
)
{
...
@@ -107,7 +75,7 @@ object ProfileManager {
...
@@ -107,7 +75,7 @@ object ProfileManager {
}
}
}
}
fun
serializeToJson
(
profiles
:
List
<
Profile
>?
=
getA
ll
Profiles
()):
JSONArray
?
{
fun
serializeToJson
(
profiles
:
List
<
Profile
>?
=
getA
ctive
Profiles
()):
JSONArray
?
{
if
(
profiles
==
null
)
return
null
if
(
profiles
==
null
)
return
null
val
lookup
=
LongSparseArray
<
Profile
>(
profiles
.
size
).
apply
{
profiles
.
forEach
{
put
(
it
.
id
,
it
)
}
}
val
lookup
=
LongSparseArray
<
Profile
>(
profiles
.
size
).
apply
{
profiles
.
forEach
{
put
(
it
.
id
,
it
)
}
}
return
JSONArray
(
profiles
.
map
{
it
.
toJson
(
lookup
)
}.
toTypedArray
())
return
JSONArray
(
profiles
.
map
{
it
.
toJson
(
lookup
)
}.
toTypedArray
())
...
@@ -159,9 +127,19 @@ object ProfileManager {
...
@@ -159,9 +127,19 @@ object ProfileManager {
if
(!
nonEmpty
)
DataStore
.
profileId
=
createProfile
().
id
if
(!
nonEmpty
)
DataStore
.
profileId
=
createProfile
().
id
}
}
@Throws
(
IOException
::
class
)
fun
getActiveProfiles
():
List
<
Profile
>?
=
try
{
PrivateDatabase
.
profileDao
.
listActive
()
}
catch
(
ex
:
SQLiteCantOpenDatabaseException
)
{
throw
IOException
(
ex
)
}
catch
(
ex
:
SQLException
)
{
printLog
(
ex
)
null
}
@Throws
(
IOException
::
class
)
@Throws
(
IOException
::
class
)
fun
getAllProfiles
():
List
<
Profile
>?
=
try
{
fun
getAllProfiles
():
List
<
Profile
>?
=
try
{
PrivateDatabase
.
profileDao
.
list
()
PrivateDatabase
.
profileDao
.
list
All
()
}
catch
(
ex
:
SQLiteCantOpenDatabaseException
)
{
}
catch
(
ex
:
SQLiteCantOpenDatabaseException
)
{
throw
IOException
(
ex
)
throw
IOException
(
ex
)
}
catch
(
ex
:
SQLException
)
{
}
catch
(
ex
:
SQLException
)
{
...
...
core/src/main/java/com/github/shadowsocks/subscription/SubscriptionService.kt
0 → 100644
View file @
4126b313
/*******************************************************************************
* *
* Copyright (C) 2020 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2020 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.subscription
import
android.app.NotificationChannel
import
android.app.NotificationManager
import
android.app.Service
import
android.content.Intent
import
android.os.IBinder
import
android.widget.Toast
import
androidx.annotation.RequiresApi
import
androidx.core.app.NotificationCompat
import
androidx.core.content.ContextCompat
import
androidx.lifecycle.MutableLiveData
import
com.github.shadowsocks.Core
import
com.github.shadowsocks.Core.app
import
com.github.shadowsocks.core.R
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.utils.*
import
com.google.gson.JsonStreamParser
import
kotlinx.coroutines.*
import
java.io.File
import
java.io.IOException
import
java.io.InputStream
import
java.net.HttpURLConnection
import
java.net.URL
class
SubscriptionService
:
Service
()
{
companion
object
{
private
const
val
NOTIFICATION_CHANNEL
=
"service-subscription"
private
const
val
NOTIFICATION_ID
=
2
private
var
worker
:
Job
?
=
null
val
idle
=
MutableLiveData
<
Boolean
>(
true
)
val
notificationChannel
@RequiresApi
(
26
)
get
()
=
NotificationChannel
(
NOTIFICATION_CHANNEL
,
app
.
getText
(
R
.
string
.
service_subscription
),
NotificationManager
.
IMPORTANCE_LOW
)
}
override
fun
onBind
(
intent
:
Intent
?):
IBinder
?
=
null
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
if
(
worker
==
null
)
{
idle
.
value
=
false
worker
=
GlobalScope
.
launch
{
val
urls
=
Subscription
.
instance
.
urls
val
notification
=
NotificationCompat
.
Builder
(
this
@SubscriptionService
,
NOTIFICATION_CHANNEL
).
apply
{
color
=
ContextCompat
.
getColor
(
this
@SubscriptionService
,
R
.
color
.
material_primary_500
)
priority
=
NotificationCompat
.
PRIORITY_LOW
setCategory
(
NotificationCompat
.
CATEGORY_PROGRESS
)
setContentTitle
(
getString
(
R
.
string
.
service_subscription_working
))
setOngoing
(
true
)
setProgress
(
urls
.
size
(),
0
,
false
)
setSmallIcon
(
R
.
drawable
.
ic_file_cloud_download
)
setWhen
(
0
)
}
Core
.
notification
.
notify
(
NOTIFICATION_ID
,
notification
.
build
())
counter
=
0
val
workers
=
urls
.
asIterable
().
map
{
url
->
async
(
Dispatchers
.
IO
)
{
work
(
url
,
urls
.
size
(),
notification
)
}
}
try
{
val
localJsons
=
workers
.
awaitAll
()
withContext
(
Dispatchers
.
Main
)
{
Core
.
notification
.
notify
(
NOTIFICATION_ID
,
notification
.
apply
{
setProgress
(
0
,
0
,
true
)
}.
build
())
createProfilesFromSubscription
(
localJsons
.
asSequence
().
filterNotNull
().
map
{
it
.
inputStream
()
})
}
}
finally
{
for
(
worker
in
workers
)
{
worker
.
cancel
()
try
{
worker
.
getCompleted
()
?.
apply
{
if
(!
delete
())
deleteOnExit
()
}
}
catch
(
_
:
Exception
)
{
}
}
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
Core
.
notification
.
cancel
(
NOTIFICATION_ID
)
idle
.
value
=
true
}
check
(
worker
!=
null
)
worker
=
null
stopSelf
(
startId
)
}
}
}
else
stopSelf
(
startId
)
return
START_NOT_STICKY
}
private
var
counter
=
0
private
suspend
fun
work
(
url
:
URL
,
max
:
Int
,
notification
:
NotificationCompat
.
Builder
):
File
?
{
val
tempFile
=
File
.
createTempFile
(
"subscription-"
,
".json"
,
cacheDir
)
try
{
(
url
.
openConnection
()
as
HttpURLConnection
).
useCancellable
{
tempFile
.
outputStream
().
use
{
out
->
inputStream
.
copyTo
(
out
)
}
}
return
tempFile
}
catch
(
e
:
IOException
)
{
e
.
printStackTrace
()
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
Toast
.
makeText
(
this
@SubscriptionService
,
e
.
readableMessage
,
Toast
.
LENGTH_LONG
).
show
()
}
if
(!
tempFile
.
delete
())
tempFile
.
deleteOnExit
()
return
null
}
finally
{
withContext
(
Dispatchers
.
Main
)
{
counter
+=
1
Core
.
notification
.
notify
(
NOTIFICATION_ID
,
notification
.
apply
{
setProgress
(
max
,
counter
,
false
)
}.
build
())
}
}
}
private
fun
createProfilesFromSubscription
(
jsons
:
Sequence
<
InputStream
>)
{
val
currentId
=
DataStore
.
profileId
val
profiles
=
ProfileManager
.
getAllProfiles
()
val
subscriptions
=
mutableMapOf
<
Pair
<
String
?
,
String
>,
Profile
>()
val
toUpdate
=
mutableSetOf
<
Long
>()
var
feature
:
Profile
?
=
null
profiles
?.
forEach
{
profile
->
// preprocessing phase
if
(
currentId
==
profile
.
id
)
feature
=
profile
if
(
profile
.
subscription
==
Profile
.
SubscriptionStatus
.
UserConfigured
)
return
@forEach
if
(
subscriptions
.
putIfAbsentCompat
(
profile
.
name
to
profile
.
formattedAddress
,
profile
)
!=
null
)
{
ProfileManager
.
delProfile
(
profile
.
id
)
if
(
currentId
==
profile
.
id
)
DataStore
.
profileId
=
0
}
else
if
(
profile
.
subscription
==
Profile
.
SubscriptionStatus
.
Active
)
{
toUpdate
.
add
(
profile
.
id
)
profile
.
subscription
=
Profile
.
SubscriptionStatus
.
Obsolete
}
}
for
(
json
in
jsons
.
asIterable
())
try
{
Profile
.
parseJson
(
JsonStreamParser
(
json
.
bufferedReader
()).
asSequence
().
single
(),
feature
)
{
subscriptions
.
computeCompat
(
it
.
name
to
it
.
formattedAddress
)
{
_
,
oldProfile
->
when
(
oldProfile
?.
subscription
)
{
Profile
.
SubscriptionStatus
.
Active
->
oldProfile
// skip dup subscription
Profile
.
SubscriptionStatus
.
Obsolete
->
{
oldProfile
.
password
=
it
.
password
oldProfile
.
method
=
it
.
method
oldProfile
.
plugin
=
it
.
plugin
oldProfile
.
udpFallback
=
it
.
udpFallback
oldProfile
.
subscription
=
Profile
.
SubscriptionStatus
.
Active
oldProfile
}
else
->
ProfileManager
.
createProfile
(
it
.
apply
{
subscription
=
Profile
.
SubscriptionStatus
.
Active
})
}
}
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
Toast
.
makeText
(
this
,
e
.
readableMessage
,
Toast
.
LENGTH_LONG
).
show
()
}
profiles
?.
forEach
{
profile
->
if
(
toUpdate
.
contains
(
profile
.
id
))
ProfileManager
.
updateProfile
(
profile
)
}
}
override
fun
onDestroy
()
{
worker
?.
cancel
()
super
.
onDestroy
()
}
}
core/src/main/java/com/github/shadowsocks/utils/Utils.kt
View file @
4126b313
...
@@ -89,8 +89,18 @@ fun String?.parseNumericAddress(): InetAddress? = Os.inet_pton(OsConstants.AF_IN
...
@@ -89,8 +89,18 @@ fun String?.parseNumericAddress(): InetAddress? = Os.inet_pton(OsConstants.AF_IN
if
(
Build
.
VERSION
.
SDK_INT
>=
29
)
it
else
parseNumericAddress
.
invoke
(
null
,
this
)
as
InetAddress
if
(
Build
.
VERSION
.
SDK_INT
>=
29
)
it
else
parseNumericAddress
.
invoke
(
null
,
this
)
as
InetAddress
}
}
fun
<
K
,
V
>
MutableMap
<
K
,
V
>.
computeIfAbsentCompat
(
key
:
K
,
value
:
()
->
V
)
=
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
fun
<
K
,
V
>
MutableMap
<
K
,
V
>.
computeCompat
(
key
:
K
,
remappingFunction
:
(
K
,
V
?)
->
V
?)
=
if
(
Build
.
VERSION
.
SDK_INT
<
24
)
{
computeIfAbsent
(
key
)
{
value
()
}
else
this
[
key
]
?:
value
().
also
{
put
(
key
,
it
)
}
val
oldValue
=
get
(
key
)
remappingFunction
(
key
,
oldValue
).
also
{
newValue
->
if
(
newValue
!=
null
)
put
(
key
,
newValue
)
else
if
(
oldValue
!=
null
||
containsKey
(
key
))
remove
(
key
)
}
}
else
compute
(
key
)
{
k
,
oldValue
->
remappingFunction
(
k
,
oldValue
)
}
fun
<
K
,
V
>
MutableMap
<
K
,
V
>.
computeIfAbsentCompat
(
key
:
K
,
value
:
()
->
V
)
=
if
(
Build
.
VERSION
.
SDK_INT
<
24
)
{
this
[
key
]
?:
value
().
also
{
put
(
key
,
it
)
}
}
else
computeIfAbsent
(
key
)
{
value
()
}
fun
<
K
,
V
>
MutableMap
<
K
,
V
>.
putIfAbsentCompat
(
key
:
K
,
value
:
V
)
=
if
(
Build
.
VERSION
.
SDK_INT
<
24
)
{
this
[
key
]
?:
put
(
key
,
value
)
}
else
putIfAbsent
(
key
,
value
)
suspend
fun
<
T
>
HttpURLConnection
.
useCancellable
(
block
:
suspend
HttpURLConnection
.()
->
T
):
T
{
suspend
fun
<
T
>
HttpURLConnection
.
useCancellable
(
block
:
suspend
HttpURLConnection
.()
->
T
):
T
{
return
suspendCancellableCoroutine
{
cont
->
return
suspendCancellableCoroutine
{
cont
->
...
...
mobile/src/main/res/drawable/ic_action
_cloud_download.xml
→
core/src/main/res/drawable/ic_file
_cloud_download.xml
View file @
4126b313
File moved
core/src/main/res/values/strings.xml
View file @
4126b313
...
@@ -140,6 +140,9 @@
...
@@ -140,6 +140,9 @@
<string
name=
"add_subscription"
>
Add a subscription
</string>
<string
name=
"add_subscription"
>
Add a subscription
</string>
<string
name=
"edit_subscription"
>
Edit subscription
</string>
<string
name=
"edit_subscription"
>
Edit subscription
</string>
<string
name=
"update_subscription"
>
Refresh servers from subscription
</string>
<string
name=
"update_subscription"
>
Refresh servers from subscription
</string>
<string
name=
"service_subscription"
>
Subscription Service
</string>
<string
name=
"service_subscription_working"
>
Syncing subscriptions… (%d of %d)
</string>
<string
name=
"service_subscription_finishing"
>
Finishing up…
</string>
<!-- acl -->
<!-- acl -->
<string
name=
"custom_rules"
>
Custom rules
</string>
<string
name=
"custom_rules"
>
Custom rules
</string>
...
...
mobile/build.gradle
View file @
4126b313
...
@@ -70,7 +70,6 @@ dependencies {
...
@@ -70,7 +70,6 @@ dependencies {
implementation
'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation
'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation
'me.zhanghai.android.fastscroll:library:1.1.0'
implementation
'me.zhanghai.android.fastscroll:library:1.1.0'
implementation
'xyz.belvi.mobilevision:barcodescanner:2.0.3'
implementation
'xyz.belvi.mobilevision:barcodescanner:2.0.3'
implementation
'me.zhanghai.android.materialprogressbar:library:1.6.1'
testImplementation
"junit:junit:$junitVersion"
testImplementation
"junit:junit:$junitVersion"
androidTestImplementation
"androidx.test:runner:$androidTestVersion"
androidTestImplementation
"androidx.test:runner:$androidTestVersion"
androidTestImplementation
"androidx.test.espresso:espresso-core:$androidEspressoVersion"
androidTestImplementation
"androidx.test.espresso:espresso-core:$androidEspressoVersion"
...
...
mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
View file @
4126b313
...
@@ -126,9 +126,9 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
...
@@ -126,9 +126,9 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
findPreference
<
Preference
>(
Key
.
password
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
password
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
method
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
method
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
remotePort
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
remotePort
)
!!
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
plugin
)
!!
.
isEnabled
=
false
plugin
.
isEnabled
=
false
findPreference
<
Preference
>(
Key
.
pluginConfigure
)
!!
.
isEnabled
=
false
pluginConfigure
.
isEnabled
=
false
udpFallback
.
isEnabled
=
false
}
}
}
}
...
...
mobile/src/main/java/com/github/shadowsocks/ProfilesFragment.kt
View file @
4126b313
...
@@ -344,7 +344,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
...
@@ -344,7 +344,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
}
}
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>(),
ProfileManager
.
Listener
{
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>(),
ProfileManager
.
Listener
{
internal
val
profiles
=
ProfileManager
.
getA
ll
Profiles
()
?.
toMutableList
()
?:
mutableListOf
()
internal
val
profiles
=
ProfileManager
.
getA
ctive
Profiles
()
?.
toMutableList
()
?:
mutableListOf
()
private
val
updated
=
HashSet
<
Profile
>()
private
val
updated
=
HashSet
<
Profile
>()
init
{
init
{
...
@@ -543,7 +543,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
...
@@ -543,7 +543,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
true
true
}
}
R
.
id
.
action_export_clipboard
->
{
R
.
id
.
action_export_clipboard
->
{
val
profiles
=
ProfileManager
.
getA
ll
Profiles
()
val
profiles
=
ProfileManager
.
getA
ctive
Profiles
()
(
activity
as
MainActivity
).
snackbar
().
setText
(
if
(
profiles
!=
null
)
{
(
activity
as
MainActivity
).
snackbar
().
setText
(
if
(
profiles
!=
null
)
{
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
profiles
.
joinToString
(
"\n"
)))
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
profiles
.
joinToString
(
"\n"
)))
R
.
string
.
action_export_msg
R
.
string
.
action_export_msg
...
...
mobile/src/main/java/com/github/shadowsocks/UdpFallbackProfileActivity.kt
View file @
4126b313
...
@@ -64,7 +64,7 @@ class UdpFallbackProfileActivity : AppCompatActivity() {
...
@@ -64,7 +64,7 @@ class UdpFallbackProfileActivity : AppCompatActivity() {
}
}
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
internal
val
profiles
=
(
ProfileManager
.
getA
ll
Profiles
()
?.
toMutableList
()
?:
mutableListOf
())
internal
val
profiles
=
(
ProfileManager
.
getA
ctive
Profiles
()
?.
toMutableList
()
?:
mutableListOf
())
.
filter
{
it
.
id
!=
editingId
&&
PluginConfiguration
(
it
.
plugin
?:
""
).
selected
.
isEmpty
()
}
.
filter
{
it
.
id
!=
editingId
&&
PluginConfiguration
(
it
.
plugin
?:
""
).
selected
.
isEmpty
()
}
override
fun
onBindViewHolder
(
holder
:
ProfileViewHolder
,
position
:
Int
)
=
override
fun
onBindViewHolder
(
holder
:
ProfileViewHolder
,
position
:
Int
)
=
...
...
mobile/src/main/java/com/github/shadowsocks/subscription/SubscriptionFragment.kt
View file @
4126b313
...
@@ -33,6 +33,7 @@ import android.widget.EditText
...
@@ -33,6 +33,7 @@ import android.widget.EditText
import
android.widget.TextView
import
android.widget.TextView
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.widget.Toolbar
import
androidx.appcompat.widget.Toolbar
import
androidx.lifecycle.observe
import
androidx.recyclerview.widget.DefaultItemAnimator
import
androidx.recyclerview.widget.DefaultItemAnimator
import
androidx.recyclerview.widget.ItemTouchHelper
import
androidx.recyclerview.widget.ItemTouchHelper
import
androidx.recyclerview.widget.LinearLayoutManager
import
androidx.recyclerview.widget.LinearLayoutManager
...
@@ -41,23 +42,14 @@ import com.github.shadowsocks.MainActivity
...
@@ -41,23 +42,14 @@ import com.github.shadowsocks.MainActivity
import
com.github.shadowsocks.R
import
com.github.shadowsocks.R
import
com.github.shadowsocks.ToolbarFragment
import
com.github.shadowsocks.ToolbarFragment
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.plugin.AlertDialogFragment
import
com.github.shadowsocks.plugin.AlertDialogFragment
import
com.github.shadowsocks.utils.asIterable
import
com.github.shadowsocks.utils.readableMessage
import
com.github.shadowsocks.utils.readableMessage
import
com.github.shadowsocks.utils.useCancellable
import
com.github.shadowsocks.widget.ListHolderListener
import
com.github.shadowsocks.widget.ListHolderListener
import
com.github.shadowsocks.widget.MainListListener
import
com.github.shadowsocks.widget.MainListListener
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.google.android.material.textfield.TextInputLayout
import
com.google.android.material.textfield.TextInputLayout
import
kotlinx.android.parcel.Parcelize
import
kotlinx.android.parcel.Parcelize
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.launch
import
me.zhanghai.android.fastscroll.FastScrollerBuilder
import
me.zhanghai.android.fastscroll.FastScrollerBuilder
import
me.zhanghai.android.materialprogressbar.MaterialProgressBar
import
java.net.HttpURLConnection
import
java.net.MalformedURLException
import
java.net.MalformedURLException
import
java.net.URL
import
java.net.URL
...
@@ -89,7 +81,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
...
@@ -89,7 +81,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
inputLayout
=
view
.
findViewById
(
R
.
id
.
content_layout
)
inputLayout
=
view
.
findViewById
(
R
.
id
.
content_layout
)
editText
.
setText
(
arg
.
item
)
editText
.
setText
(
arg
.
item
)
editText
.
addTextChangedListener
(
this
@SubDialogFragment
)
editText
.
addTextChangedListener
(
this
@SubDialogFragment
)
setTitle
(
R
.
string
.
add
_subscription
)
setTitle
(
R
.
string
.
edit
_subscription
)
setPositiveButton
(
android
.
R
.
string
.
ok
,
listener
)
setPositiveButton
(
android
.
R
.
string
.
ok
,
listener
)
setNegativeButton
(
android
.
R
.
string
.
cancel
,
null
)
setNegativeButton
(
android
.
R
.
string
.
cancel
,
null
)
if
(
arg
.
item
.
isNotEmpty
())
setNeutralButton
(
R
.
string
.
delete
,
listener
)
if
(
arg
.
item
.
isNotEmpty
())
setNeutralButton
(
R
.
string
.
delete
,
listener
)
...
@@ -219,55 +211,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
...
@@ -219,55 +211,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
private
val
adapter
by
lazy
{
SubscriptionAdapter
()
}
private
val
adapter
by
lazy
{
SubscriptionAdapter
()
}
private
lateinit
var
list
:
RecyclerView
private
lateinit
var
list
:
RecyclerView
private
lateinit
var
progress
:
MaterialProgressBar
private
var
mode
:
ActionMode
?
=
null
private
var
mode
:
ActionMode
?
=
null
private
lateinit
var
undoManager
:
UndoSnackbarManager
<
Any
>
private
lateinit
var
undoManager
:
UndoSnackbarManager
<
URL
>
private
var
fetchJob
:
Job
?
=
null
private
fun
fetchServerFromSubscriptions
()
{
if
(
fetchJob
?.
isActive
!=
true
)
{
val
activity
=
activity
as
MainActivity
progress
.
visibility
=
View
.
VISIBLE
fetchJob
=
GlobalScope
.
launch
{
val
subscription
=
Subscription
.
instance
val
oldProfiles
=
ProfileManager
.
getAllProfiles
()
var
replace
=
true
for
(
url
in
subscription
.
urls
.
asIterable
())
{
try
{
val
connection
=
url
.
openConnection
()
as
HttpURLConnection
connection
.
useCancellable
{
ProfileManager
.
createProfilesFromSubscription
(
sequenceOf
(
connection
.
inputStream
),
replace
,
oldProfiles
)
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
activity
.
snackbar
(
e
.
readableMessage
).
show
()
}
finally
{
replace
=
false
}
}
val
userProfiles
=
oldProfiles
?.
filter
{
it
.
subscription
==
Profile
.
SubscriptionStatus
.
UserConfigured
}
if
(
userProfiles
!=
null
)
{
for
(
profile
in
userProfiles
.
asIterable
())
{
ProfileManager
.
createProfile
(
profile
)
}
}
progress
.
post
{
progress
.
visibility
=
View
.
INVISIBLE
}
}
}
}
override
fun
onPause
()
{
fetchJob
?.
cancel
()
super
.
onPause
()
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
=
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
=
inflater
.
inflate
(
R
.
layout
.
layout_subscriptions
,
container
,
false
)
inflater
.
inflate
(
R
.
layout
.
layout_subscriptions
,
container
,
false
)
...
@@ -278,13 +223,15 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
...
@@ -278,13 +223,15 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
toolbar
.
setTitle
(
R
.
string
.
subscriptions
)
toolbar
.
setTitle
(
R
.
string
.
subscriptions
)
toolbar
.
inflateMenu
(
R
.
menu
.
subscription_menu
)
toolbar
.
inflateMenu
(
R
.
menu
.
subscription_menu
)
toolbar
.
setOnMenuItemClickListener
(
this
)
toolbar
.
setOnMenuItemClickListener
(
this
)
SubscriptionService
.
idle
.
observe
(
this
)
{
toolbar
.
menu
.
findItem
(
R
.
id
.
action_update_subscription
).
isEnabled
=
it
}
val
activity
=
activity
as
MainActivity
val
activity
=
activity
as
MainActivity
list
=
view
.
findViewById
(
R
.
id
.
list
)
list
=
view
.
findViewById
(
R
.
id
.
list
)
list
.
setOnApplyWindowInsetsListener
(
MainListListener
)
list
.
setOnApplyWindowInsetsListener
(
MainListListener
)
list
.
layoutManager
=
LinearLayoutManager
(
activity
,
RecyclerView
.
VERTICAL
,
false
)
list
.
layoutManager
=
LinearLayoutManager
(
activity
,
RecyclerView
.
VERTICAL
,
false
)
list
.
itemAnimator
=
DefaultItemAnimator
()
list
.
itemAnimator
=
DefaultItemAnimator
()
list
.
adapter
=
adapter
list
.
adapter
=
adapter
progress
=
view
.
findViewById
(
R
.
id
.
indeterminate_horizontal_progress
)
FastScrollerBuilder
(
list
).
useMd2Style
().
build
()
FastScrollerBuilder
(
list
).
useMd2Style
().
build
()
undoManager
=
UndoSnackbarManager
(
activity
,
adapter
::
undo
)
undoManager
=
UndoSnackbarManager
(
activity
,
adapter
::
undo
)
ItemTouchHelper
(
object
:
ItemTouchHelper
.
SimpleCallback
(
0
,
ItemTouchHelper
.
START
or
ItemTouchHelper
.
END
)
{
ItemTouchHelper
(
object
:
ItemTouchHelper
.
SimpleCallback
(
0
,
ItemTouchHelper
.
START
or
ItemTouchHelper
.
END
)
{
...
@@ -313,7 +260,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
...
@@ -313,7 +260,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
true
true
}
}
R
.
id
.
action_update_subscription
->
{
R
.
id
.
action_update_subscription
->
{
fetchServerFromSubscriptions
()
val
context
=
requireContext
()
context
.
startService
(
Intent
(
context
,
SubscriptionService
::
class
.
java
))
true
true
}
}
else
->
false
else
->
false
...
...
mobile/src/main/java/com/github/shadowsocks/tasker/ConfigActivity.kt
View file @
4126b313
...
@@ -71,7 +71,7 @@ class ConfigActivity : AppCompatActivity() {
...
@@ -71,7 +71,7 @@ class ConfigActivity : AppCompatActivity() {
}
}
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
internal
val
profiles
=
ProfileManager
.
getA
ll
Profiles
()
?.
toMutableList
()
?:
mutableListOf
()
internal
val
profiles
=
ProfileManager
.
getA
ctive
Profiles
()
?.
toMutableList
()
?:
mutableListOf
()
override
fun
onBindViewHolder
(
holder
:
ProfileViewHolder
,
position
:
Int
)
=
override
fun
onBindViewHolder
(
holder
:
ProfileViewHolder
,
position
:
Int
)
=
if
(
position
==
0
)
holder
.
bindDefault
()
else
holder
.
bind
(
profiles
[
position
-
1
])
if
(
position
==
0
)
holder
.
bindDefault
()
else
holder
.
bind
(
profiles
[
position
-
1
])
...
...
mobile/src/main/res/drawable/ic_cloud_queue.xml
→
mobile/src/main/res/drawable/ic_
file_
cloud_queue.xml
View file @
4126b313
File moved
mobile/src/main/res/layout/layout_profile.xml
View file @
4126b313
...
@@ -43,7 +43,7 @@
...
@@ -43,7 +43,7 @@
android:focusable=
"true"
android:focusable=
"true"
android:padding=
"12dp"
android:padding=
"12dp"
android:visibility=
"gone"
android:visibility=
"gone"
app:srcCompat=
"@drawable/ic_cloud_queue"
/>
app:srcCompat=
"@drawable/ic_
file_
cloud_queue"
/>
<androidx.appcompat.widget.AppCompatImageView
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/edit"
android:id=
"@+id/edit"
...
...
mobile/src/main/res/layout/layout_subscriptions.xml
View file @
4126b313
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<LinearLayout
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"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
...
@@ -9,18 +8,6 @@
...
@@ -9,18 +8,6 @@
<include
layout=
"@layout/toolbar_light_dark"
/>
<include
layout=
"@layout/toolbar_light_dark"
/>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id=
"@+id/indeterminate_horizontal_progress"
style=
"@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"bottom"
android:indeterminate=
"true"
android:visibility=
"invisible"
app:mpb_progressStyle=
"horizontal"
app:mpb_showProgressBackground=
"false"
app:mpb_useIntrinsicPadding=
"false"
/>
<FrameLayout
<FrameLayout
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_height=
"0dp"
...
...
mobile/src/main/res/menu/navigation_main.xml
View file @
4126b313
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
android:icon=
"@drawable/ic_action_description"
/>
android:icon=
"@drawable/ic_action_description"
/>
<item
android:id=
"@+id/subscriptions"
<item
android:id=
"@+id/subscriptions"
android:title=
"@string/subscriptions"
android:title=
"@string/subscriptions"
android:icon=
"@drawable/ic_
action
_cloud_download"
/>
android:icon=
"@drawable/ic_
file
_cloud_download"
/>
<item
android:id=
"@+id/customRules"
<item
android:id=
"@+id/customRules"
android:title=
"@string/custom_rules"
android:title=
"@string/custom_rules"
android:icon=
"@drawable/ic_action_assignment"
/>
android:icon=
"@drawable/ic_action_assignment"
/>
...
...
mobile/src/main/res/menu/subscription_menu.xml
View file @
4126b313
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
app:showAsAction=
"always"
/>
app:showAsAction=
"always"
/>
<item
android:title=
"@string/update_subscription"
<item
android:title=
"@string/update_subscription"
android:id=
"@+id/action_update_subscription"
android:id=
"@+id/action_update_subscription"
android:icon=
"@drawable/ic_
action
_cloud_download"
android:icon=
"@drawable/ic_
file
_cloud_download"
android:alphabeticShortcut=
"r"
android:alphabeticShortcut=
"r"
app:showAsAction=
"ifRoom"
/>
app:showAsAction=
"ifRoom"
/>
</menu>
</menu>
tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt
View file @
4126b313
...
@@ -209,7 +209,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
...
@@ -209,7 +209,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
private
fun
populateProfiles
()
{
private
fun
populateProfiles
()
{
ProfileManager
.
ensureNotEmpty
()
ProfileManager
.
ensureNotEmpty
()
val
profiles
=
ProfileManager
.
getA
ll
Profiles
()
!!
val
profiles
=
ProfileManager
.
getA
ctive
Profiles
()
!!
fab
.
value
=
null
fab
.
value
=
null
fab
.
entries
=
profiles
.
map
{
it
.
formattedName
}.
toTypedArray
()
fab
.
entries
=
profiles
.
map
{
it
.
formattedName
}.
toTypedArray
()
fab
.
entryValues
=
profiles
.
map
{
it
.
id
.
toString
()
}.
toTypedArray
()
fab
.
entryValues
=
profiles
.
map
{
it
.
id
.
toString
()
}.
toTypedArray
()
...
...
tv/src/main/java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt
View file @
4126b313
...
@@ -52,7 +52,7 @@ class ProfilesDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
...
@@ -52,7 +52,7 @@ class ProfilesDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
}
}
}
}
private
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
private
inner
class
ProfilesAdapter
:
RecyclerView
.
Adapter
<
ProfileViewHolder
>()
{
val
profiles
=
ProfileManager
.
getA
ll
Profiles
()
!!
val
profiles
=
ProfileManager
.
getA
ctive
Profiles
()
!!
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
)
=
ProfileViewHolder
(
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
)
=
ProfileViewHolder
(
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
leanback_list_preference_item_single_2
,
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
leanback_list_preference_item_single_2
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment