Compare commits

...

32 Commits

Author SHA1 Message Date
2dust
62d0951a24 Merge pull request #1208 from yuhan6665/fix-viewpager
Fix an issue with ViewPage2 and menu bar
2021-08-28 17:06:21 +08:00
2dust
d6605cc866 Merge pull request #1204 from douo/master
Add support in subscription url for http basic authentication.
2021-08-28 17:06:10 +08:00
yuhan6665
1fc493d879 Fix an issue with ViewPage2 and menu bar 2021-08-27 17:48:45 -04:00
tiou
bdc27dd180 Use built-in urlDecode. 2021-08-23 16:58:48 +08:00
tiou
e257c4cb56 Subscription support http basic authentication in url. 2021-08-23 16:49:35 +08:00
2dust
e9d2ed98af Merge pull request #1198 from yuhan6665/user-agent
Set user agent to v2rayNG/version when update subscription
2021-08-21 19:19:20 +08:00
yuhan6665
901a82eb54 Set user agent to v2rayNG/version when update subscription 2021-08-20 18:29:34 -04:00
2dust
26cc29944b Merge pull request #1183 from yuhan6665/fix-drag
Fix a bug when user delete item right after drag it
2021-08-17 20:20:56 +08:00
yuhan6665
7052546f2b Fix a bug when user delete item right after drag it 2021-08-13 18:04:43 -04:00
2dust
ae2b10ede7 Merge pull request #1177 from yuhan6665/fix-base64
Fix parsing for some loosely formatted base64
2021-08-10 13:09:35 +08:00
2dust
0df051e640 Merge pull request #1175 from jiuqianyuan/master
Add prompts for update subscription error
2021-08-10 13:06:14 +08:00
jiuqianyuan
79aa86a402 Update MainActivity.kt 2021-08-09 17:22:49 +08:00
yuhan6665
7bf32c2b30 Fix parsing for some loosely formatted base64 2021-08-07 17:35:09 -04:00
jiuqianyuan
ceb1c55e49 Fix missing prompts for update subscription error 2021-08-07 08:08:45 +08:00
2dust
4ab3134eac Merge pull request #1164 from yuhan6665/update
Fix compile warnings
2021-07-31 15:38:19 +08:00
yuhan6665
0391685d42 Fix some more compile warnings 2021-07-30 18:31:29 -04:00
yuhan6665
6482253697 Fix some compile warnings
remove flatDir
2021-07-30 18:31:29 -04:00
yuhan6665
c033358126 Fix some compile warnings
Migrate to Androidx Activity Result APIs
2021-07-30 18:31:29 -04:00
yuhan6665
0b52c78b72 Fix some compile warnings
Some deprecated methods
2021-07-30 18:31:29 -04:00
yuhan6665
a2a7d790e7 Fix some compile warnings
Migrate to viewpager2
2021-07-30 18:31:29 -04:00
yuhan6665
c7c6564624 Fix some compile warnings
upper case and lower case
2021-07-30 18:31:29 -04:00
2dust
f7d534fc00 Merge pull request #1153 from yuhan6665/update
Migrate to View Binding
2021-07-26 08:07:28 +08:00
yuhan6665
ecb1d58e7a Migrate to View Binding
Use view binding to simplify some code
2021-07-23 18:17:17 -04:00
2dust
54a191d181 Merge pull request #1147 from yuhan6665/update
Update project dependencies
2021-07-20 08:12:30 +08:00
2dust
90b6979f94 Merge pull request #1146 from jiuqianyuan/master
fix update subscription error
2021-07-20 08:12:18 +08:00
jiuqianyuan
5daf4886b7 fix wrong commit
wrong commit for dbee208
2021-07-17 16:18:33 +08:00
jiuqianyuan
dbee2085fb clean useless “” 2021-07-17 15:58:29 +08:00
yuhan6665
3c6c4ca3a5 Update project dependencies 2021-07-16 18:11:28 -04:00
jiuqianyuan
3932ee6e9b fix update subscription error
Prevent server list from being emptied when update subscription failed or the subscription url is invalid
2021-07-16 17:49:56 +08:00
2dust
242c96a4de Merge pull request #1136 from yuhan6665/EditorKit
Editor kit
2021-07-14 08:26:14 +08:00
yuhan6665
a23245435f Use EditorKit for custom config
https://github.com/massivemadness/Squircle-IDE
2021-07-09 20:45:48 -04:00
yuhan6665
57476290d3 Auto Migrate to AndroidX 2021-07-09 20:44:38 -04:00
48 changed files with 464 additions and 707 deletions

View File

@@ -3,7 +3,7 @@
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
[![API](https://img.shields.io/badge/API-17%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/jelly-bean#android-4.2)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.4.10-blue.svg)](https://kotlinlang.org)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.5.10-blue.svg)](https://kotlinlang.org)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases)

View File

@@ -64,33 +64,39 @@ android {
1000000 + android.defaultConfig.versionCode
}
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
testImplementation 'junit:junit:4.13'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
// Androidx
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.3.0"
implementation "com.google.android.material:material:1.4.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.preference:preference:1.0.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
// Androidx ktx
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:livedata:1.1.1'
implementation 'androidx.activity:activity-ktx:1.2.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
// Android support library
implementation "com.android.support:support-v4:$supportLibVersion"
implementation "com.android.support:appcompat-v7:$supportLibVersion"
implementation "com.android.support:design:$supportLibVersion"
implementation "com.android.support:cardview-v7:$supportLibVersion"
implementation "com.android.support:preference-v7:$supportLibVersion"
implementation "com.android.support:recyclerview-v7:$supportLibVersion"
implementation "com.android.support:multidex:1.0.3"
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
//kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
// DSL
implementation 'com.tencent:mmkv-static:1.0.19'
implementation 'com.tencent:mmkv-static:1.2.7'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'io.reactivex:rxjava:1.3.4'
implementation 'io.reactivex:rxandroid:1.2.1'
@@ -99,9 +105,8 @@ dependencies {
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation(name: 'libv2ray', ext: 'aar')
//implementation(name: 'tun2socks', ext: 'aar')
implementation 'com.blacksquircle.ui:editorkit:2.0.0'
implementation 'com.blacksquircle.ui:language-json:2.0.0'
}
buildscript {
@@ -114,8 +119,3 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion"
}
}
repositories {
flatDir {
dirs 'libs'
}
}

View File

@@ -28,6 +28,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<uses-sdk tools:overrideLibrary="com.blacksquircle.ui.editorkit, com.blacksquircle.ui.language.json, com.blacksquircle.ui.language.base"/>
<application
android:name=".AngApplication"
android:allowBackup="true"

View File

@@ -16,8 +16,8 @@
package com.v2ray.ang.helper;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
/**
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.

View File

@@ -16,7 +16,7 @@
package com.v2ray.ang.helper;
import android.support.v7.widget.helper.ItemTouchHelper;
import androidx.recyclerview.widget.ItemTouchHelper;
/**
* Interface to notify an item ViewHolder of relevant callbacks from {@link

View File

@@ -16,7 +16,7 @@
package com.v2ray.ang.helper;
import android.support.v7.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
/**
* Listener for manual initiation of a drag.

View File

@@ -17,9 +17,11 @@
package com.v2ray.ang.helper;
import android.graphics.Canvas;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import org.jetbrains.annotations.NotNull;
/**
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
@@ -52,7 +54,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
public int getMovementFlags(RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
@@ -66,24 +68,25 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
public boolean onMove(@NotNull RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
// Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
mAdapter.onItemMove(source.getBindingAdapterPosition(), target.getBindingAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
mAdapter.onItemDismiss(viewHolder.getBindingAdapterPosition());
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
public void onChildDraw(@NotNull Canvas c, @NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Fade out the view as it is swiped out of the parent's bounds
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
@@ -109,7 +112,7 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
public void clearView(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
mAdapter.onItemMoveCompleted();

View File

@@ -1,7 +1,7 @@
package com.v2ray.ang
import android.support.multidex.MultiDexApplication
import android.support.v7.preference.PreferenceManager
import androidx.multidex.MultiDexApplication
import androidx.preference.PreferenceManager
import com.tencent.mmkv.MMKV
class AngApplication : MultiDexApplication() {

View File

@@ -21,7 +21,7 @@ data class ServerConfig(
return ServerConfig(
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.toLowerCase(),
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean(
users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))),
@@ -32,7 +32,7 @@ data class ServerConfig(
return ServerConfig(
configType = configType,
outboundBean = V2rayConfig.OutboundBean(
protocol = configType.name.toLowerCase(),
protocol = configType.name.lowercase(),
settings = V2rayConfig.OutboundBean.OutSettingsBean(
servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())),
streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean()))

View File

@@ -1,244 +0,0 @@
package com.v2ray.ang.extension
import android.app.Fragment
import android.app.ProgressDialog
import android.content.Context
import android.content.DialogInterface
import android.database.Cursor
import android.graphics.drawable.Drawable
import android.support.v7.app.AlertDialog
import android.view.KeyEvent
import android.view.View
import android.widget.ListAdapter
fun Context.alertView(
title: String? = null,
view: View,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
if (title != null) customView(view)
if (init != null) init()
}
fun Fragment.alert(
message: String,
title: String? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = activity.alert(message, title, init)
fun Context.alert(
message: String,
title: String? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
message(message)
if (init != null) init()
}
fun Fragment.alert(
message: Int,
title: Int? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = activity.alert(message, title, init)
fun Context.alert(
message: Int,
title: Int? = null,
init: (KAlertDialogBuilder.() -> Unit)? = null
) = KAlertDialogBuilder(this).apply {
if (title != null) title(title)
message(message)
if (init != null) init()
}
fun Fragment.alert(init: KAlertDialogBuilder.() -> Unit): KAlertDialogBuilder = activity.alert(init)
fun Context.alert(init: KAlertDialogBuilder.() -> Unit) = KAlertDialogBuilder(this).apply { init() }
fun Fragment.progressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.progressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(false, message?.let { getString(it) }, title?.let { getString(it) }, init)
fun Fragment.indeterminateProgressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.indeterminateProgressDialog(
message: Int? = null,
title: Int? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(true, message?.let { getString(it) }, title?.let { getString(it) }, init)
fun Fragment.progressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.progressDialog(message, title, init)
fun Context.progressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(false, message, title, init)
fun Fragment.indeterminateProgressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = activity.indeterminateProgressDialog(message, title, init)
fun Context.indeterminateProgressDialog(
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = progressDialog(true, message, title, init)
private fun Context.progressDialog(
indeterminate: Boolean,
message: String? = null,
title: String? = null,
init: (ProgressDialog.() -> Unit)? = null
) = ProgressDialog(this).apply {
isIndeterminate = indeterminate
if (!indeterminate) setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
if (message != null) setMessage(message)
if (title != null) setTitle(title)
if (init != null) init()
show()
}
fun Fragment.selector(
title: CharSequence? = null,
items: List<CharSequence>,
onClick: (Int) -> Unit
): Unit = activity.selector(title, items, onClick)
fun Context.selector(
title: CharSequence? = null,
items: List<CharSequence>,
onClick: (Int) -> Unit
) {
with(KAlertDialogBuilder(this)) {
if (title != null) title(title)
items(items, onClick)
show()
}
}
class KAlertDialogBuilder(val ctx: Context) {
val builder: AlertDialog.Builder = AlertDialog.Builder(ctx)
protected var dialog: AlertDialog? = null
fun dismiss() {
dialog?.dismiss()
}
fun show(): KAlertDialogBuilder {
dialog = builder.create()
dialog!!.show()
return this
}
fun title(title: CharSequence) {
builder.setTitle(title)
}
fun title(resource: Int) {
builder.setTitle(resource)
}
fun message(title: CharSequence) {
builder.setMessage(title)
}
fun message(resource: Int) {
builder.setMessage(resource)
}
fun icon(icon: Int) {
builder.setIcon(icon)
}
fun icon(icon: Drawable) {
builder.setIcon(icon)
}
fun customTitle(title: View) {
builder.setCustomTitle(title)
}
fun customView(view: View) {
builder.setView(view)
}
fun cancellable(value: Boolean = true) {
builder.setCancelable(value)
}
fun onCancel(f: () -> Unit) {
builder.setOnCancelListener { f() }
}
fun onKey(f: (keyCode: Int, e: KeyEvent) -> Boolean) {
builder.setOnKeyListener({ dialog, keyCode, event -> f(keyCode, event) })
}
fun neutralButton(textResource: Int = android.R.string.ok, f: DialogInterface.() -> Unit = { dismiss() }) {
neutralButton(ctx.getString(textResource), f)
}
fun neutralButton(title: String, f: DialogInterface.() -> Unit = { dismiss() }) {
builder.setNeutralButton(title, { dialog, which -> dialog.f() })
}
fun positiveButton(textResource: Int = android.R.string.ok, f: DialogInterface.() -> Unit) {
positiveButton(ctx.getString(textResource), f)
}
fun positiveButton(title: String, f: DialogInterface.() -> Unit) {
builder.setPositiveButton(title, { dialog, which -> dialog.f() })
}
fun negativeButton(textResource: Int = android.R.string.cancel, f: DialogInterface.() -> Unit = { dismiss() }) {
negativeButton(ctx.getString(textResource), f)
}
fun negativeButton(title: String, f: DialogInterface.() -> Unit = { dismiss() }) {
builder.setNegativeButton(title, { dialog, which -> dialog.f() })
}
fun items(itemsId: Int, f: (which: Int) -> Unit) {
items(ctx.resources!!.getTextArray(itemsId), f)
}
fun items(items: List<CharSequence>, f: (which: Int) -> Unit) {
items(items.toTypedArray(), f)
}
fun items(items: Array<CharSequence>, f: (which: Int) -> Unit) {
builder.setItems(items, { dialog, which -> f(which) })
}
fun adapter(adapter: ListAdapter, f: (which: Int) -> Unit) {
builder.setAdapter(adapter, { dialog, which -> f(which) })
}
fun adapter(cursor: Cursor, labelColumn: String, f: (which: Int) -> Unit) {
builder.setCursor(cursor, { dialog, which -> f(which) }, labelColumn)
}
}

View File

@@ -10,8 +10,8 @@ import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.Build
import android.support.annotation.RequiresApi
import android.support.v4.app.NotificationCompat
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig

View File

@@ -8,7 +8,7 @@ import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import android.os.StrictMode
import android.support.annotation.RequiresApi
import androidx.annotation.RequiresApi
import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig

View File

@@ -1,6 +1,6 @@
package com.v2ray.ang.ui
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.view.MenuItem
abstract class BaseActivity : AppCompatActivity() {
@@ -11,4 +11,4 @@ abstract class BaseActivity : AppCompatActivity() {
}
else -> super.onOptionsItemSelected(item)
}
}
}

View File

@@ -1,21 +1,17 @@
package com.v2ray.ang.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter
class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List<Fragment>) :
FragmentStateAdapter(fragmentActivity) {
class FragmentAdapter(fm: FragmentManager, private val mFragments: List<Fragment>, private val mTitles: List<String>) : FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
override fun createFragment(position: Int): Fragment {
return mFragments[position]
}
override fun getCount(): Int {
override fun getItemCount(): Int {
return mFragments.size
}
override fun getPageTitle(position: Int): CharSequence? {
return mTitles[position]
}
}
}

View File

@@ -9,9 +9,9 @@ import android.view.MenuItem
import android.view.View
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_logcat.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -19,10 +19,13 @@ import java.io.IOException
import java.util.LinkedHashSet
class LogcatActivity : BaseActivity() {
private lateinit var binding: ActivityLogcatBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_logcat)
binding = ActivityLogcatBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_logcat)
@@ -33,7 +36,7 @@ class LogcatActivity : BaseActivity() {
private fun logcat(shouldFlushLog: Boolean) {
try {
pb_waiting.visibility = View.VISIBLE
binding.pbWaiting.visibility = View.VISIBLE
GlobalScope.launch(Dispatchers.Default) {
if (shouldFlushLog) {
@@ -56,10 +59,10 @@ class LogcatActivity : BaseActivity() {
// val allText = bufferedReader.use(BufferedReader::readText)
val allText = process.inputStream.bufferedReader().use { it.readText() }
launch(Dispatchers.Main) {
tv_logcat.text = allText
tv_logcat.movementMethod = ScrollingMovementMethod()
pb_waiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { sv_logcat.fullScroll(View.FOCUS_DOWN) }
binding.tvLogcat.text = allText
binding.tvLogcat.movementMethod = ScrollingMovementMethod()
binding.pbWaiting.visibility = View.GONE
Handler(Looper.getMainLooper()).post { binding.svLogcat.fullScroll(View.FOCUS_DOWN) }
}
}
} catch (e: IOException) {
@@ -74,7 +77,7 @@ class LogcatActivity : BaseActivity() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.copy_all -> {
Utils.setClipboard(this, tv_logcat.text.toString())
Utils.setClipboard(this, binding.tvLogcat.text.toString())
toast(R.string.toast_success)
true
}

View File

@@ -1,28 +1,30 @@
package com.v2ray.ang.ui
import android.Manifest
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.net.Uri
import android.net.VpnService
import android.os.Bundle
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.helper.ItemTouchHelper
import com.google.android.material.navigation.NavigationView
import androidx.core.view.GravityCompat
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import android.text.TextUtils
import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityMainBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
@@ -31,7 +33,6 @@ import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -43,26 +44,28 @@ import java.net.URL
import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
companion object {
private const val REQUEST_CODE_VPN_PREPARE = 0
private const val REQUEST_SCAN = 1
private const val REQUEST_FILE_CHOOSER = 2
private const val REQUEST_SCAN_URL = 3
}
private lateinit var binding: ActivityMainBinding
private val adapter by lazy { MainRecyclerAdapter(this) }
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
startV2Ray()
}
}
private var mItemTouchHelper: ItemTouchHelper? = null
val mainViewModel: MainViewModel by lazy { ViewModelProviders.of(this).get(MainViewModel::class.java) }
val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_server)
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
fab.setOnClickListener {
binding.fab.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
Utils.stopVService(this)
} else if (settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN" == "VPN") {
@@ -70,36 +73,36 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
if (intent == null) {
startV2Ray()
} else {
startActivityForResult(intent, REQUEST_CODE_VPN_PREPARE)
requestVpnPermission.launch(intent)
}
} else {
startV2Ray()
}
}
layout_test.setOnClickListener {
binding.layoutTest.setOnClickListener {
if (mainViewModel.isRunning.value == true) {
tv_test_state.text = getString(R.string.connection_test_testing)
binding.tvTestState.text = getString(R.string.connection_test_testing)
mainViewModel.testCurrentServerRealPing()
} else {
// tv_test_state.text = getString(R.string.connection_test_fail)
}
}
recycler_view.setHasFixedSize(true)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = adapter
val callback = SimpleItemTouchHelperCallback(adapter)
mItemTouchHelper = ItemTouchHelper(callback)
mItemTouchHelper?.attachToRecyclerView(recycler_view)
mItemTouchHelper?.attachToRecyclerView(binding.recyclerView)
val toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawer_layout.addDrawerListener(toggle)
this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
binding.drawerLayout.addDrawerListener(toggle)
toggle.syncState()
nav_view.setNavigationItemSelectedListener(this)
version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
binding.navView.setNavigationItemSelectedListener(this)
binding.version.text = "v${BuildConfig.VERSION_NAME} (${Libv2ray.checkVersionX()})"
setupViewModelObserver()
migrateLegacy()
@@ -114,18 +117,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
adapter.notifyDataSetChanged()
}
})
mainViewModel.updateTestResultAction.observe(this, { tv_test_state.text = it })
mainViewModel.updateTestResultAction.observe(this, { binding.tvTestState.text = it })
mainViewModel.isRunning.observe(this, {
val isRunning = it ?: return@observe
adapter.isRunning = isRunning
if (isRunning) {
fab.setImageResource(R.drawable.ic_v)
tv_test_state.text = getString(R.string.connection_connected)
layout_test.isFocusable = true
binding.fab.setImageResource(R.drawable.ic_v)
binding.tvTestState.text = getString(R.string.connection_connected)
binding.layoutTest.isFocusable = true
} else {
fab.setImageResource(R.drawable.ic_v_idle)
tv_test_state.text = getString(R.string.connection_not_connected)
layout_test.isFocusable = false
binding.fab.setImageResource(R.drawable.ic_v_idle)
binding.tvTestState.text = getString(R.string.connection_not_connected)
binding.layoutTest.isFocusable = false
}
hideCircle()
})
@@ -167,30 +170,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
super.onPause()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_VPN_PREPARE ->
if (resultCode == RESULT_OK) {
startV2Ray()
}
REQUEST_SCAN ->
if (resultCode == RESULT_OK) {
importBatchConfig(data?.getStringExtra("SCAN_RESULT"))
}
REQUEST_FILE_CHOOSER -> {
val uri = data?.data
if (resultCode == RESULT_OK && uri != null) {
readContentFromUri(uri)
}
}
REQUEST_SCAN_URL ->
if (resultCode == RESULT_OK) {
importConfigCustomUrl(data?.getStringExtra("SCAN_RESULT"))
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
@@ -198,7 +177,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.import_qrcode -> {
importQRcode(REQUEST_SCAN)
importQRcode(true)
true
}
R.id.import_clipboard -> {
@@ -233,7 +212,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.import_config_custom_url_scan -> {
importQRcode(REQUEST_SCAN_URL)
importQRcode(false)
true
}
@@ -276,7 +255,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
/**
* import config from qrcode
*/
fun importQRcode(requestCode: Int): Boolean {
fun importQRcode(forConfig: Boolean): Boolean {
// try {
// startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
// .addCategory(Intent.CATEGORY_DEFAULT)
@@ -286,7 +265,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
if (forConfig)
scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java))
else
scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java))
else
toast(R.string.toast_permission_denied)
}
@@ -294,6 +276,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return true
}
private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"))
}
}
private val scanQRCodeForUrlToCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
importConfigCustomUrl(it.data?.getStringExtra("SCAN_RESULT"))
}
}
/**
* import config from clipboard
*/
@@ -377,7 +371,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
URL(url).readText()
Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) {
e.printStackTrace()
""
@@ -414,10 +408,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Log.d(ANG_PACKAGE, url)
GlobalScope.launch(Dispatchers.IO) {
val configText = try {
URL(url).readText()
Utils.getUrlContentWithCustomUserAgent(url)
} catch (e: Exception) {
e.printStackTrace()
""
launch(Dispatchers.Main) {
toast("\"" + it.second.remarks + "\" " + getString(R.string.toast_failure))
}
return@launch
}
launch(Dispatchers.Main) {
importBatchConfig(Utils.decode(configText), it.first)
@@ -440,14 +437,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
intent.addCategory(Intent.CATEGORY_OPENABLE)
try {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.title_file_chooser)),
REQUEST_FILE_CHOOSER)
chooseFileForCustomConfig.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager)
}
}
private val chooseFileForCustomConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val uri = it.data?.data
if (it.resultCode == RESULT_OK && uri != null) {
readContentFromUri(uri)
}
}
/**
* read content from uri
*/
@@ -505,7 +507,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
fun showCircle() {
fabProgressCircle?.show()
binding.fabProgressCircle.show()
}
fun hideCircle() {
@@ -513,8 +515,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
Observable.timer(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (fabProgressCircle.isShown) {
fabProgressCircle.hide()
if (binding.fabProgressCircle.isShown) {
binding.fabProgressCircle.hide()
}
}
} catch (e: Exception) {
@@ -522,8 +524,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
override fun onBackPressed() {
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
drawer_layout.closeDrawer(GravityCompat.START)
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
@@ -553,7 +555,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
startActivity(Intent(this, LogcatActivity::class.java))
}
}
drawer_layout.closeDrawer(GravityCompat.START)
binding.drawerLayout.closeDrawer(GravityCompat.START)
return true
}
}

View File

@@ -2,9 +2,9 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.widget.RecyclerView
import androidx.core.content.ContextCompat
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -12,6 +12,9 @@ import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerFooterBinding
import com.v2ray.ang.databinding.ItemRecyclerMainBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toast
@@ -21,8 +24,6 @@ import com.v2ray.ang.service.V2RayServiceManager
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.item_qrcode.view.*
import kotlinx.android.synthetic.main.item_recycler_main.view.*
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit
@@ -51,34 +52,34 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
val outbound = config.getProxyOutbound()
val aff = MmkvManager.decodeServerAffiliationInfo(guid)
holder.name.text = config.remarks
holder.radio.isChecked = guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
holder.itemMainBinding.tvName.text = config.remarks
holder.itemMainBinding.btnRadio.isChecked = guid == mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.test_result.text = aff?.getTestDelayString() ?: ""
holder.itemMainBinding.tvTestResult.text = aff?.getTestDelayString() ?: ""
if (aff?.testDelayMillis?:0L < 0L) {
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark))
holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, android.R.color.holo_red_dark))
} else {
holder.test_result.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing))
holder.itemMainBinding.tvTestResult.setTextColor(ContextCompat.getColor(mActivity, R.color.colorPing))
}
holder.subscription.text = ""
holder.itemMainBinding.tvSubscription.text = ""
val json = subStorage?.decodeString(config.subscriptionId)
if (!json.isNullOrBlank()) {
val sub = Gson().fromJson(json, SubscriptionItem::class.java)
holder.subscription.text = sub.remarks
holder.itemMainBinding.tvSubscription.text = sub.remarks
}
var shareOptions = share_method.asList()
if (config.configType == EConfigType.CUSTOM) {
holder.type.text = mActivity.getString(R.string.server_customize_config)
holder.itemMainBinding.tvType.text = mActivity.getString(R.string.server_customize_config)
shareOptions = shareOptions.takeLast(1)
} else if (config.configType == EConfigType.VLESS) {
holder.type.text = config.configType.name
holder.itemMainBinding.tvType.text = config.configType.name
} else {
holder.type.text = config.configType.name.toLowerCase()
holder.itemMainBinding.tvType.text = config.configType.name.lowercase()
}
holder.statistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"
holder.itemMainBinding.tvStatistics.text = "${outbound?.getServerAddress()} : ${outbound?.getServerPort()}"
holder.layout_share.setOnClickListener {
holder.itemMainBinding.layoutShare.setOnClickListener {
AlertDialog.Builder(mActivity).setItems(shareOptions.toTypedArray()) { _, i ->
try {
when (i) {
@@ -86,9 +87,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (config.configType == EConfigType.CUSTOM) {
shareFullContent(guid)
} else {
val iv = LayoutInflater.from(mActivity).inflate(R.layout.item_qrcode, null)
iv.iv_qcode.setImageBitmap(AngConfigManager.share2QRCode(guid))
AlertDialog.Builder(mActivity).setView(iv).show()
val ivBinding = ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity))
ivBinding.ivQcode.setImageBitmap(AngConfigManager.share2QRCode(guid))
AlertDialog.Builder(mActivity).setView(ivBinding.root).show()
}
}
1 -> {
@@ -107,7 +108,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}.show()
}
holder.layout_edit.setOnClickListener {
holder.itemMainBinding.layoutEdit.setOnClickListener {
val intent = Intent().putExtra("guid", guid)
.putExtra("isRunning", isRunning)
if (config.configType == EConfigType.CUSTOM) {
@@ -116,7 +117,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java))
}
}
holder.layout_remove.setOnClickListener {
holder.itemMainBinding.layoutRemove.setOnClickListener {
if (guid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) {
mActivity.mainViewModel.removeServer(guid)
notifyItemRemoved(position)
@@ -124,7 +125,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
}
holder.infoContainer.setOnClickListener {
holder.itemMainBinding.infoContainer.setOnClickListener {
val selected = mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)
if (guid != selected) {
mainStorage?.encode(MmkvManager.KEY_SELECTED_SERVER, guid)
@@ -146,9 +147,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (holder is FooterViewHolder) {
//if (activity?.defaultDPreference?.getPrefBoolean(AppConfig.PREF_INAPP_BUY_IS_PREMIUM, false)) {
if (true) {
holder.layout_edit.visibility = View.INVISIBLE
holder.itemFooterBinding.layoutEdit.visibility = View.INVISIBLE
} else {
holder.layout_edit.setOnClickListener {
holder.itemFooterBinding.layoutEdit.setOnClickListener {
Utils.openUri(mActivity, "${Utils.decode(AppConfig.promotionUrl)}?t=${System.currentTimeMillis()}")
}
}
@@ -166,11 +167,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType) {
VIEW_TYPE_ITEM ->
MainViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_recycler_main, parent, false))
MainViewHolder(ItemRecyclerMainBinding.inflate(LayoutInflater.from(parent.context), parent, false))
else ->
FooterViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_recycler_footer, parent, false))
FooterViewHolder(ItemRecyclerFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
}
@@ -182,40 +181,21 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
}
}
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainViewHolder(itemView: View) : BaseViewHolder(itemView), ItemTouchHelperViewHolder {
val subscription = itemView.tv_subscription!!
val radio = itemView.btn_radio!!
val name = itemView.tv_name!!
val test_result = itemView.tv_test_result!!
val type = itemView.tv_type!!
val statistics = itemView.tv_statistics!!
val infoContainer = itemView.info_container!!
val layout_edit = itemView.layout_edit!!
val layout_share = itemView.layout_share
val layout_remove = itemView.layout_remove!!
override fun onItemSelected() {
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY)
}
override fun onItemClear() {
fun onItemClear() {
itemView.setBackgroundColor(0)
}
}
class FooterViewHolder(itemView: View) : BaseViewHolder(itemView), ItemTouchHelperViewHolder {
val layout_edit = itemView.layout_edit!!
class MainViewHolder(val itemMainBinding: ItemRecyclerMainBinding) :
BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder
override fun onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY)
}
override fun onItemClear() {
itemView.setBackgroundColor(0)
}
}
class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) :
BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder
override fun onItemDismiss(position: Int) {
val guid = mActivity.mainViewModel.serverList.getOrNull(position) ?: return
@@ -233,7 +213,11 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
mActivity.mainViewModel.swapServer(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
//notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1)
// position is changed, since position is used by click callbacks, need to update range
if (toPosition > fromPosition)
notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1)
else
notifyItemRangeChanged(toPosition, fromPosition - toPosition + 1)
return true
}

View File

@@ -4,10 +4,10 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.os.Bundle
import android.support.v7.preference.PreferenceManager
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.text.TextUtils
import android.util.Log
import android.view.Menu
@@ -17,7 +17,6 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import com.v2ray.ang.R
import com.v2ray.ang.util.AppManagerUtil
import kotlinx.android.synthetic.main.activity_bypass_list.*
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import java.text.Collator
@@ -26,6 +25,7 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.databinding.ActivityBypassListBinding
import com.v2ray.ang.dto.AppInfo
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.v2RayApplication
@@ -36,6 +36,7 @@ import kotlinx.coroutines.launch
import java.net.URL
class PerAppProxyActivity : BaseActivity() {
private lateinit var binding: ActivityBypassListBinding
private var adapter: PerAppProxyAdapter? = null
private var appsAll: List<AppInfo>? = null
@@ -43,12 +44,14 @@ class PerAppProxyActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bypass_list)
binding = ActivityBypassListBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
recycler_view.addItemDecoration(dividerItemDecoration)
binding.recyclerView.addItemDecoration(dividerItemDecoration)
val blacklist = defaultSharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, null)
@@ -90,20 +93,20 @@ class PerAppProxyActivity : BaseActivity() {
.subscribe {
appsAll = it
adapter = PerAppProxyAdapter(this, it, blacklist)
recycler_view.adapter = adapter
pb_waiting.visibility = View.GONE
binding.recyclerView.adapter = adapter
binding.pbWaiting.visibility = View.GONE
}
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() {
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
var dst = 0
val threshold = resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 3
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
dst += dy
if (dst > threshold) {
header_view.hide()
binding.headerView.hide()
dst = 0
} else if (dst < -20) {
header_view.show()
binding.headerView.show()
dst = 0
}
}
@@ -139,23 +142,23 @@ class PerAppProxyActivity : BaseActivity() {
}
})
switch_per_app_proxy.setOnCheckedChangeListener { _, isChecked ->
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_PER_APP_PROXY, isChecked).apply()
}
switch_per_app_proxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
binding.switchPerAppProxy.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_PER_APP_PROXY, false)
switch_bypass_apps.setOnCheckedChangeListener { _, isChecked ->
binding.switchBypassApps.setOnCheckedChangeListener { _, isChecked ->
defaultSharedPreferences.edit().putBoolean(AppConfig.PREF_BYPASS_APPS, isChecked).apply()
}
switch_bypass_apps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false)
binding.switchBypassApps.isChecked = defaultSharedPreferences.getBoolean(AppConfig.PREF_BYPASS_APPS, false)
et_search.setOnEditorActionListener { v, actionId, event ->
binding.etSearch.setOnEditorActionListener { v, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
//hide
var imm: InputMethodManager = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS)
val key = v.text.toString().toUpperCase()
val key = v.text.toString().uppercase()
val apps = ArrayList<AppInfo>()
if (TextUtils.isEmpty(key)) {
appsAll?.forEach {
@@ -163,13 +166,13 @@ class PerAppProxyActivity : BaseActivity() {
}
} else {
appsAll?.forEach {
if (it.appName.toUpperCase().indexOf(key) >= 0) {
if (it.appName.uppercase().indexOf(key) >= 0) {
apps.add(it)
}
}
}
adapter = PerAppProxyAdapter(this, apps, adapter?.blacklist)
recycler_view.adapter = adapter
binding.recyclerView.adapter = adapter
adapter?.notifyDataSetChanged()
true
} else {
@@ -247,7 +250,7 @@ class PerAppProxyActivity : BaseActivity() {
adapter?.blacklist!!.clear()
if (switch_bypass_apps.isChecked) {
if (binding.switchBypassApps.isChecked) {
adapter?.let {
it.apps.forEach block@{
val packageName = it.packageName

View File

@@ -1,13 +1,13 @@
package com.v2ray.ang.ui
import android.graphics.Color
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemRecyclerBypassListBinding
import com.v2ray.ang.dto.AppInfo
import kotlinx.android.synthetic.main.item_recycler_bypass_list.view.*
import java.util.*
class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, blacklist: MutableSet<String>?) :
@@ -18,8 +18,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
private const val VIEW_TYPE_ITEM = 1
}
private var mActivity: BaseActivity = activity
val blacklist = if (blacklist == null) HashSet<String>() else HashSet<String>(blacklist)
val blacklist = if (blacklist == null) HashSet() else HashSet(blacklist)
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is AppViewHolder) {
@@ -43,8 +42,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
// VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater
// .inflate(R.layout.item_recycler_bypass_list, parent, false))
else -> AppViewHolder(LayoutInflater.from(ctx)
.inflate(R.layout.item_recycler_bypass_list, parent, false))
else -> AppViewHolder(ItemRecyclerBypassListBinding.inflate(LayoutInflater.from(ctx), parent, false))
}
}
@@ -53,30 +51,25 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
inner class AppViewHolder(itemView: View) : BaseViewHolder(itemView),
inner class AppViewHolder(private val itemBypassBinding: ItemRecyclerBypassListBinding) : BaseViewHolder(itemBypassBinding.root),
View.OnClickListener {
private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName)
private lateinit var appInfo: AppInfo
val icon = itemView.icon!!
val name = itemView.name!!
val package_name = itemView.package_name!!
val checkBox = itemView.check_box!!
fun bind(appInfo: AppInfo) {
this.appInfo = appInfo
icon.setImageDrawable(appInfo.appIcon)
itemBypassBinding.icon.setImageDrawable(appInfo.appIcon)
// name.text = appInfo.appName
checkBox.isChecked = inBlacklist
package_name.text = appInfo.packageName
itemBypassBinding.checkBox.isChecked = inBlacklist
itemBypassBinding.packageName.text = appInfo.packageName
if (appInfo.isSystemApp) {
name.text = String.format("** %1s", appInfo.appName)
name.setTextColor(Color.RED)
itemBypassBinding.name.text = String.format("** %1s", appInfo.appName)
itemBypassBinding.name.setTextColor(Color.RED)
} else {
name.text = appInfo.appName
name.setTextColor(Color.DKGRAY)
itemBypassBinding.name.text = appInfo.appName
itemBypassBinding.name.setTextColor(Color.DKGRAY)
}
itemView.setOnClickListener(this)
@@ -85,10 +78,10 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List<AppInfo>, bl
override fun onClick(v: View?) {
if (inBlacklist) {
blacklist.remove(appInfo.packageName)
checkBox.isChecked = false
itemBypassBinding.checkBox.isChecked = false
} else {
blacklist.add(appInfo.packageName)
checkBox.isChecked = true
itemBypassBinding.checkBox.isChecked = true
}
}
}

View File

@@ -3,19 +3,23 @@ package com.v2ray.ang.ui
import android.graphics.Color
import android.os.Bundle
import com.v2ray.ang.R
import android.support.v4.app.Fragment
import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayoutMediator
import com.v2ray.ang.AppConfig
import kotlinx.android.synthetic.main.activity_routing_settings.*
import com.v2ray.ang.databinding.ActivityRoutingSettingsBinding
class RoutingSettingsActivity : BaseActivity() {
private lateinit var binding: ActivityRoutingSettingsBinding
private val titles: Array<out String> by lazy {
resources.getStringArray(R.array.routing_tag)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_routing_settings)
binding = ActivityRoutingSettingsBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.routing_settings_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
@@ -25,9 +29,11 @@ class RoutingSettingsActivity : BaseActivity() {
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_DIRECT))
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_BLOCKED))
val adapter = FragmentAdapter(supportFragmentManager, fragments, titles.toList())
viewpager?.adapter = adapter
tablayout.setTabTextColors(Color.BLACK, Color.RED)
tablayout.setupWithViewPager(viewpager)
val adapter = FragmentAdapter(this, fragments)
binding.viewpager.adapter = adapter
binding.tablayout.setTabTextColors(Color.BLACK, Color.RED)
TabLayoutMediator(binding.tablayout, binding.viewpager) { tab, position ->
tab.text = titles[position]
}.attach()
}
}

View File

@@ -4,13 +4,14 @@ import android.Manifest
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.preference.PreferenceManager
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import android.view.*
import com.v2ray.ang.R
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.fragment_routing_settings.*
import android.view.MenuInflater
import androidx.activity.result.contract.ActivityResultContracts
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.toast
@@ -22,8 +23,6 @@ import java.net.URL
class RoutingSettingsFragment : Fragment() {
companion object {
private const val routing_arg = "routing_arg"
private const val REQUEST_SCAN_REPLACE = 11
private const val REQUEST_SCAN_APPEND = 12
}
val defaultSharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(context) }
@@ -45,7 +44,7 @@ class RoutingSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val content = defaultSharedPreferences.getString(arguments!!.getString(routing_arg), "")
val content = defaultSharedPreferences.getString(requireArguments().getString(routing_arg), "")
et_routing_content.text = Utils.getEditable(content!!)
setHasOptionsMenu(true)
@@ -59,7 +58,7 @@ class RoutingSettingsFragment : Fragment() {
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.save_routing -> {
val content = et_routing_content.text.toString()
defaultSharedPreferences.edit().putString(arguments!!.getString(routing_arg), content).apply()
defaultSharedPreferences.edit().putString(requireArguments().getString(routing_arg), content).apply()
activity?.toast(R.string.toast_success)
true
}
@@ -68,11 +67,11 @@ class RoutingSettingsFragment : Fragment() {
true
}
R.id.scan_replace -> {
scanQRcode(REQUEST_SCAN_REPLACE)
scanQRcode(true)
true
}
R.id.scan_append -> {
scanQRcode(REQUEST_SCAN_APPEND)
scanQRcode(false)
true
}
R.id.default_rules -> {
@@ -82,17 +81,20 @@ class RoutingSettingsFragment : Fragment() {
else -> super.onOptionsItemSelected(item)
}
fun scanQRcode(requestCode: Int): Boolean {
fun scanQRcode(forReplace: Boolean): Boolean {
// try {
// startActivityForResult(Intent("com.google.zxing.client.android.SCAN")
// .addCategory(Intent.CATEGORY_DEFAULT)
// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode)
// } catch (e: Exception) {
RxPermissions(activity!!)
RxPermissions(requireActivity())
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(activity, ScannerActivity::class.java), requestCode)
if (forReplace)
scanQRCodeForReplace.launch(Intent(activity, ScannerActivity::class.java))
else
scanQRCodeForAppend.launch(Intent(activity, ScannerActivity::class.java))
else
activity?.toast(R.string.toast_permission_denied)
}
@@ -100,9 +102,23 @@ class RoutingSettingsFragment : Fragment() {
return true
}
private val scanQRCodeForReplace = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val content = it.data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable(content!!)
}
}
private val scanQRCodeForAppend = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val content = it.data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable("${et_routing_content.text},$content")
}
}
fun setDefaultRules(): Boolean {
var url = AppConfig.v2rayCustomRoutingListUrl
when (arguments!!.getString(routing_arg)) {
when (requireArguments().getString(routing_arg)) {
AppConfig.PREF_V2RAY_ROUTING_AGENT -> {
url += AppConfig.TAG_AGENT
}
@@ -129,22 +145,4 @@ class RoutingSettingsFragment : Fragment() {
}
return true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_SCAN_REPLACE ->
if (resultCode == RESULT_OK) {
val content = data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable(content!!)
}
REQUEST_SCAN_APPEND ->
if (resultCode == RESULT_OK) {
val content = data?.getStringExtra("SCAN_RESULT")
et_routing_content.text = Utils.getEditable("${et_routing_content.text},$content")
}
}
}
}

View File

@@ -6,25 +6,23 @@ import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import com.v2ray.ang.extension.toast
class ScScannerActivity : BaseActivity() {
companion object {
private const val REQUEST_SCAN = 1
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_none)
importQRcode(REQUEST_SCAN)
importQRcode()
}
fun importQRcode(requestCode: Int): Boolean {
fun importQRcode(): Boolean {
RxPermissions(this)
.request(Manifest.permission.CAMERA)
.subscribe {
if (it)
startActivityForResult(Intent(this, ScannerActivity::class.java), requestCode)
scanQRCode.launch(Intent(this, ScannerActivity::class.java))
else
toast(R.string.toast_permission_denied)
}
@@ -32,21 +30,16 @@ class ScScannerActivity : BaseActivity() {
return true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_SCAN ->
if (resultCode == RESULT_OK) {
val count = AngConfigManager.importBatchConfig(data?.getStringExtra("SCAN_RESULT"), "")
if (count > 0) {
toast(R.string.toast_success)
} else {
toast(R.string.toast_failure)
}
startActivity(Intent(this, MainActivity::class.java))
}
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "")
if (count > 0) {
toast(R.string.toast_success)
} else {
toast(R.string.toast_failure)
}
startActivity(Intent(this, MainActivity::class.java))
}
finish()
}
}

View File

@@ -9,6 +9,7 @@ import android.content.Intent
import android.graphics.BitmapFactory
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
import com.google.zxing.BarcodeFormat
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
@@ -16,10 +17,6 @@ import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.QRCodeDecoder
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
companion object {
private const val REQUEST_FILE_CHOOSER = 2
}
private var mScannerView: ZXingScannerView? = null
@@ -59,7 +56,7 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
// mScannerView!!.resumeCameraPreview(this)
}
fun finished(text: String) {
private fun finished(text: String) {
val intent = Intent()
intent.putExtra("SCAN_RESULT", text)
setResult(Activity.RESULT_OK, intent)
@@ -97,30 +94,22 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
//intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
try {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.title_file_chooser)),
REQUEST_FILE_CHOOSER)
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_FILE_CHOOSER -> {
val uri = data?.data
if (resultCode == RESULT_OK && uri != null) {
try {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
finished(text)
} catch (e: Exception) {
e.printStackTrace()
toast(e.message.toString())
}
}
private val chooseFile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val uri = it.data?.data
if (it.resultCode == RESULT_OK && uri != null) {
try {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri))
val text = QRCodeDecoder.syncDecodeQRCode(bitmap)
finished(text)
} catch (e: Exception) {
e.printStackTrace()
toast(e.message.toString())
}
}
}

View File

@@ -1,7 +1,7 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import androidx.appcompat.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem

View File

@@ -1,24 +1,26 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.blacksquircle.ui.language.json.JsonLanguage
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_server_custom_config.*
import me.drakeet.support.toast.ToastCompat
class ServerCustomConfigActivity : BaseActivity() {
private lateinit var binding: ActivityServerCustomConfigBinding
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
@@ -31,9 +33,12 @@ class ServerCustomConfigActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_server_custom_config)
binding = ActivityServerCustomConfigBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_server)
binding.editor.language = JsonLanguage()
val config = MmkvManager.decodeServerConfig(editGuid)
if (config != null) {
bindingServer(config)
@@ -47,12 +52,12 @@ class ServerCustomConfigActivity : BaseActivity() {
* bingding seleced server config
*/
private fun bindingServer(config: ServerConfig): Boolean {
et_remarks.text = Utils.getEditable(config.remarks)
binding.etRemarks.text = Utils.getEditable(config.remarks)
val raw = serverRawStorage?.decodeString(editGuid)
if (raw.isNullOrBlank()) {
tv_content.text = Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty())
binding.editor.setTextContent(Utils.getEditable(config.fullConfig?.toPrettyPrinting().orEmpty()))
} else {
tv_content.text = Utils.getEditable(raw)
binding.editor.setTextContent(Utils.getEditable(raw))
}
return true
}
@@ -61,7 +66,7 @@ class ServerCustomConfigActivity : BaseActivity() {
* clear or init server config
*/
private fun clearServer(): Boolean {
et_remarks.text = null
binding.etRemarks.text = null
return true
}
@@ -69,13 +74,13 @@ class ServerCustomConfigActivity : BaseActivity() {
* save server config
*/
private fun saveServer(): Boolean {
if (TextUtils.isEmpty(et_remarks.text.toString())) {
if (TextUtils.isEmpty(binding.etRemarks.text.toString())) {
toast(R.string.server_lab_remarks)
return false
}
val v2rayConfig = try {
Gson().fromJson(tv_content.text.toString(), V2rayConfig::class.java)
Gson().fromJson(binding.editor.text.toString(), V2rayConfig::class.java)
} catch (e: Exception) {
e.printStackTrace()
ToastCompat.makeText(this, "${getString(R.string.toast_malformed_josn)} ${e.cause?.message}", Toast.LENGTH_LONG).show()
@@ -83,11 +88,11 @@ class ServerCustomConfigActivity : BaseActivity() {
}
val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM)
config.remarks = et_remarks.text.toString().trim()
config.remarks = binding.etRemarks.text.toString().trim()
config.fullConfig = v2rayConfig
MmkvManager.encodeServerConfig(editGuid, config)
serverRawStorage?.encode(editGuid, tv_content.text.toString())
serverRawStorage?.encode(editGuid, binding.editor.text.toString())
toast(R.string.toast_success)
finish()
return true

View File

@@ -1,11 +1,11 @@
package com.v2ray.ang.ui
import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.os.Bundle
import android.support.v7.preference.*
import androidx.preference.*
import android.text.TextUtils
import android.view.View
import androidx.activity.viewModels
import com.v2ray.ang.R
import com.v2ray.ang.AppConfig
import com.v2ray.ang.extension.toast
@@ -14,8 +14,7 @@ import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.SettingsViewModel
class SettingsActivity : BaseActivity() {
private val settingsViewModel by lazy { ViewModelProviders.of(this).get(SettingsViewModel::class.java) }
private val settingsViewModel: SettingsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -1,20 +1,21 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.support.v7.app.AlertDialog
import androidx.appcompat.app.AlertDialog
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubEditBinding
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.Utils
import kotlinx.android.synthetic.main.activity_sub_edit.*
class SubEditActivity : BaseActivity() {
private lateinit var binding: ActivitySubEditBinding
var del_config: MenuItem? = null
var save_config: MenuItem? = null
@@ -24,7 +25,9 @@ class SubEditActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_edit)
binding = ActivitySubEditBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_sub_setting)
val json = subStorage?.decodeString(editSubId)
@@ -40,8 +43,8 @@ class SubEditActivity : BaseActivity() {
* bingding seleced server config
*/
private fun bindingServer(subItem: SubscriptionItem): Boolean {
et_remarks.text = Utils.getEditable(subItem.remarks)
et_url.text = Utils.getEditable(subItem.url)
binding.etRemarks.text = Utils.getEditable(subItem.remarks)
binding.etUrl.text = Utils.getEditable(subItem.url)
return true
}
@@ -50,8 +53,8 @@ class SubEditActivity : BaseActivity() {
* clear or init server config
*/
private fun clearServer(): Boolean {
et_remarks.text = null
et_url.text = null
binding.etRemarks.text = null
binding.etUrl.text = null
return true
}
@@ -70,8 +73,8 @@ class SubEditActivity : BaseActivity() {
subItem = SubscriptionItem()
}
subItem.remarks = et_remarks.text.toString()
subItem.url = et_url.text.toString()
subItem.remarks = binding.etRemarks.text.toString()
subItem.url = binding.etUrl.text.toString()
if (TextUtils.isEmpty(subItem.remarks)) {
toast(R.string.sub_setting_remarks)

View File

@@ -4,26 +4,29 @@ import android.content.Intent
import android.view.Menu
import android.view.MenuItem
import com.v2ray.ang.R
import kotlinx.android.synthetic.main.activity_sub_setting.*
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.util.MmkvManager
class SubSettingActivity : BaseActivity() {
private lateinit var binding: ActivitySubSettingBinding
var subscriptions:List<Pair<String, SubscriptionItem>> = listOf()
private val adapter by lazy { SubSettingRecyclerAdapter(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_setting)
binding = ActivitySubSettingBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
title = getString(R.string.title_sub_setting)
recycler_view.setHasFixedSize(true)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = adapter
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

View File

@@ -2,46 +2,34 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.support.v7.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.v2ray.ang.R
import kotlinx.android.synthetic.main.item_recycler_sub_setting.view.*
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.BaseViewHolder>() {
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() {
private var mActivity: SubSettingActivity = activity
override fun getItemCount() = mActivity.subscriptions.size
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
if (holder is MainViewHolder) {
val subId = mActivity.subscriptions[position].first
val subItem = mActivity.subscriptions[position].second
holder.name.text = subItem.remarks
holder.url.text = subItem.url
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val subId = mActivity.subscriptions[position].first
val subItem = mActivity.subscriptions[position].second
holder.itemSubSettingBinding.tvName.text = subItem.remarks
holder.itemSubSettingBinding.tvUrl.text = subItem.url
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.layout_edit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
.putExtra("subId", subId)
)
}
holder.itemSubSettingBinding.layoutEdit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
.putExtra("subId", subId)
)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return MainViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_recycler_sub_setting, parent, false))
}
open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class MainViewHolder(itemView: View) : BaseViewHolder(itemView) {
val name = itemView.tv_name!!
val url = itemView.tv_url!!
val layout_edit = itemView.layout_edit!!
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(ItemRecyclerSubSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
}

View File

@@ -14,10 +14,12 @@ import android.view.MenuItem
import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.databinding.ActivityTaskerBinding
import com.v2ray.ang.util.MmkvManager
import kotlinx.android.synthetic.main.activity_tasker.*
class TaskerActivity : BaseActivity() {
private lateinit var binding: ActivityTaskerBinding
private var listview: ListView? = null
private var lstData: ArrayList<String> = ArrayList()
private var lstGuid: ArrayList<String> = ArrayList()
@@ -26,7 +28,9 @@ class TaskerActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tasker)
binding = ActivityTaskerBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//add def value
lstData.add("Default")
@@ -55,7 +59,7 @@ class TaskerActivity : BaseActivity() {
if (switch == null || TextUtils.isEmpty(guid)) {
return
} else {
switch_start_service.isChecked = switch
binding.switchStartService.isChecked = switch
val pos = lstGuid.indexOf(guid.toString())
if (pos >= 0) {
listview?.setItemChecked(pos, true)
@@ -74,12 +78,12 @@ class TaskerActivity : BaseActivity() {
}
val extraBundle = Bundle()
extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, switch_start_service.isChecked)
extraBundle.putBoolean(AppConfig.TASKER_EXTRA_BUNDLE_SWITCH, binding.switchStartService.isChecked)
extraBundle.putString(AppConfig.TASKER_EXTRA_BUNDLE_GUID, lstGuid[position])
val intent = Intent()
val remarks = lstData[position]
val blurb = if (switch_start_service.isChecked) {
val blurb = if (binding.switchStartService.isChecked) {
"Start $remarks"
} else {
"Stop $remarks"

View File

@@ -3,7 +3,7 @@ package com.v2ray.ang.util
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.support.v7.preference.PreferenceManager
import androidx.preference.PreferenceManager
import android.text.TextUtils
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
@@ -245,7 +245,7 @@ object AngConfigManager {
server.address = match.groupValues[3].removeSurrounding("[", "]")
server.port = match.groupValues[4].toInt()
server.password = match.groupValues[2]
server.method = match.groupValues[1].toLowerCase()
server.method = match.groupValues[1].lowercase()
}
} else if (str.startsWith(EConfigType.SOCKS.protocolScheme)) {
var result = str.replace(EConfigType.SOCKS.protocolScheme, "")
@@ -276,7 +276,7 @@ object AngConfigManager {
server.address = match.groupValues[3].removeSurrounding("[", "]")
server.port = match.groupValues[4].toInt()
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
socksUsersBean.user = match.groupValues[1].toLowerCase()
socksUsersBean.user = match.groupValues[1].lowercase()
socksUsersBean.pass = match.groupValues[2]
server.users = listOf(socksUsersBean)
}

View File

@@ -20,6 +20,7 @@ import android.util.Patterns
import android.webkit.URLUtil
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.responseLength
import com.v2ray.ang.extension.toast
@@ -99,6 +100,15 @@ object Utils {
* base64 decode
*/
fun decode(text: String): String {
tryDecodeBase64(text)?.let { return it }
if (text.endsWith('=')) {
// try again for some loosely formatted base64
tryDecodeBase64(text.trimEnd('='))?.let { return it }
}
return ""
}
fun tryDecodeBase64(text: String): String? {
try {
return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8"))
} catch (e: Exception) {
@@ -109,7 +119,7 @@ object Utils {
} catch (e: Exception) {
Log.i(AppConfig.ANG_PACKAGE, "Parse base64 url safe failed $e")
}
return ""
return null
}
/**
@@ -447,5 +457,21 @@ object Utils {
tcpTestingSockets.clear()
}
}
@Throws(IOException::class)
fun getUrlContentWithCustomUserAgent(urlStr: String?): String {
val url = URL(urlStr)
val conn = url.openConnection()
conn.setRequestProperty("Connection", "close")
conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}")
url.userInfo?.let {
conn.setRequestProperty("Authorization",
"Basic ${encode(urlDecode(it))}")
}
conn.useCaches = false
return conn.inputStream.use {
it.bufferedReader().readText()
}
}
}

View File

@@ -1,8 +1,8 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import android.arch.lifecycle.MutableLiveData
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

View File

@@ -1,9 +1,9 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import androidx.lifecycle.AndroidViewModel
import android.content.SharedPreferences
import android.support.v7.preference.PreferenceManager
import androidx.preference.PreferenceManager
import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig

View File

@@ -13,17 +13,17 @@
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context=".ui.PerAppProxyActivity" />
<android.support.v7.widget.LinearLayoutCompat
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -37,7 +37,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_per_app_proxy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -45,7 +45,7 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<android.support.v7.widget.AppCompatTextView
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -63,7 +63,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_bypass_apps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -71,7 +71,7 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<android.support.v7.widget.AppCompatTextView
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_bypass_apps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -89,11 +89,11 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height">
<android.support.design.widget.TextInputLayout
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/et_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -103,11 +103,11 @@
android:maxLines="1"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</android.support.design.widget.TextInputLayout>
</com.google.android.material.textfield.TextInputLayout>
</RelativeLayout>
</android.support.v7.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</RelativeLayout>
</RelativeLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
@@ -12,19 +12,19 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -34,7 +34,7 @@
android:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -44,7 +44,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
@@ -83,7 +83,7 @@
android:layout_marginBottom="24dp"
android:layout_gravity="bottom|end">
<android.support.design.widget.FloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -101,11 +101,11 @@
android:nextFocusLeft="@+id/recycler_view" />
</com.github.jorgecastilloprz.FABProgressCircle>
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
</LinearLayout>
<android.support.design.widget.NavigationView
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@@ -129,7 +129,7 @@
android:gravity="center"
android:textColor="@color/accent" />
</LinearLayout>
</android.support.design.widget.NavigationView>
</com.google.android.material.navigation.NavigationView>
</android.support.v4.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>

View File

@@ -4,14 +4,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.design.widget.TabLayout
<com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@@ -8,14 +8,13 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/layout_margin_top_height">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/layout_margin_top_height">
android:layout_margin="@dimen/layout_margin_spacing">
<TextView
android:layout_width="wrap_content"
@@ -28,7 +27,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
android:layout_margin="@dimen/layout_margin_spacing">
<TextView
android:layout_width="wrap_content"
@@ -40,7 +40,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:layout_margin="@dimen/layout_margin_spacing"
android:orientation="vertical">
<TextView
@@ -59,24 +59,24 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_margin_spacing"
android:text="@string/server_lab_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
<EditText
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:text="" />
<com.blacksquircle.ui.editorkit.widget.TextProcessor
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top|start"
android:id="@+id/editor"
android:layout_marginTop="@dimen/layout_margin_top_height" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</ScrollView>

View File

@@ -8,15 +8,15 @@
android:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>

View File

@@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bypass_list_header_height">
<android.support.v7.widget.SwitchCompat
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -19,7 +19,7 @@
android:layout_centerVertical="true"
android:checked="true" />
<android.support.v7.widget.AppCompatTextView
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -45,4 +45,4 @@
android:choiceMode="singleChoice" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -7,7 +7,7 @@
android:clickable="true"
android:focusable="true">
<android.support.v7.widget.AppCompatImageView
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="46dp"
android:layout_height="46dp"
@@ -25,12 +25,12 @@
android:paddingStart="@dimen/layout_margin_right_height"
android:paddingEnd="@dimen/layout_margin_right_height">
<android.support.v7.widget.AppCompatTextView
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.AppCompatTextView
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/package_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -38,7 +38,7 @@
</LinearLayout>
<android.support.v7.widget.AppCompatCheckBox
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -49,4 +49,4 @@
android:paddingEnd="6dp"
android:paddingRight="6dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -7,7 +7,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
@@ -24,7 +24,7 @@
android:focusable="true"
android:nextFocusRight="@+id/layout_share">
<android.support.v7.widget.AppCompatRadioButton
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/btn_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -182,5 +182,5 @@
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -5,7 +5,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -102,5 +102,5 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.navigation.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_view"
android:layout_width="wrap_content"
@@ -8,4 +8,4 @@
app:headerLayout="@layout/nav_header"
app:itemTextColor="@color/colorPrimary"
app:itemIconTint="@color/colorPrimary_dark"
app:menu="@menu/menu_drawer" />
app:menu="@menu/menu_drawer" />

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://maven.google.com' }
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -13,9 +13,10 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017
kotlinVersion=1.4.10
supportLibVersion=28.0.0
kotlinVersion=1.5.10
buildToolsVer=30.0.2
compileSdkVer=30
kotlin.incremental=true
targetSdkVer=30
kotlin.incremental=true
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip