mirror of
https://github.com/2dust/v2rayNG.git
synced 2025-06-28 12:19:52 +00:00
Upgrade project to new Android Studio template and migrate Java code to Kotlin (#3937)
### Summary - Updated the project structure using the latest Android Studio template. - Migrated portions of the codebase from Java to Kotlin for improved readability and maintainability. ### Details - Refactored and reorganized files according to the new Android Studio project template to ensure compatibility with the latest project standards. - Migrated key Java classes to Kotlin, adopting Kotlin idioms and improving type safety. - Verified that core functionalities remain intact after migration and update. - Removed redundant Java files and updated imports where necessary. ### Notes - Further Kotlin migration may be needed as additional Java files are reviewed. - Test thoroughly to confirm that all functionalities work as expected after these changes.
This commit is contained in:
parent
bbf0b05b49
commit
18c0143186
104 changed files with 492 additions and 544 deletions
|
@ -1,11 +1,11 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.v2ray.ang"
|
||||
compileSdk = 34
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.v2ray.ang"
|
||||
|
@ -14,6 +14,7 @@ android {
|
|||
versionCode = 612
|
||||
versionName = "1.9.16"
|
||||
multiDexEnabled = true
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
|
@ -27,20 +28,16 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
|
||||
}
|
||||
debug {
|
||||
isMinifyEnabled = false
|
||||
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,8 +47,13 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
|
@ -69,7 +71,8 @@ android {
|
|||
|
||||
output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk"
|
||||
if (versionCodes.containsKey(abi)) {
|
||||
output.versionCodeOverride = (1000000 * versionCodes[abi]!!).plus(variant.versionCode)
|
||||
output.versionCodeOverride =
|
||||
(1000000 * versionCodes[abi]!!).plus(variant.versionCode)
|
||||
} else {
|
||||
return@forEach
|
||||
}
|
||||
|
@ -86,49 +89,60 @@ android {
|
|||
useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Core Libraries
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar", "*.jar"))))
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.org.mockito.mockito.inline)
|
||||
testImplementation(libs.mockito.kotlin)
|
||||
|
||||
implementation(libs.flexbox)
|
||||
// Androidx
|
||||
implementation(libs.constraintlayout)
|
||||
implementation(libs.legacy.support.v4)
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.cardview)
|
||||
// AndroidX Core Libraries
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.activity)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.preference.ktx)
|
||||
implementation(libs.recyclerview)
|
||||
implementation(libs.fragment.ktx)
|
||||
implementation(libs.multidex)
|
||||
implementation(libs.viewpager2)
|
||||
|
||||
// Androidx ktx
|
||||
implementation(libs.activity.ktx)
|
||||
// UI Libraries
|
||||
implementation(libs.material)
|
||||
implementation(libs.toastcompat)
|
||||
implementation(libs.editorkit)
|
||||
implementation(libs.flexbox)
|
||||
|
||||
// Data and Storage Libraries
|
||||
implementation(libs.mmkv.static)
|
||||
implementation(libs.gson)
|
||||
|
||||
// Reactive and Utility Libraries
|
||||
implementation(libs.rxjava)
|
||||
implementation(libs.rxandroid)
|
||||
implementation(libs.rxpermissions)
|
||||
|
||||
// Language and Processing Libraries
|
||||
implementation(libs.language.base)
|
||||
implementation(libs.language.json)
|
||||
|
||||
// Intent and Utility Libraries
|
||||
implementation(libs.quickie.bundled)
|
||||
implementation(libs.core)
|
||||
|
||||
// AndroidX Lifecycle and Architecture Components
|
||||
implementation(libs.lifecycle.viewmodel.ktx)
|
||||
implementation(libs.lifecycle.livedata.ktx)
|
||||
implementation(libs.lifecycle.runtime.ktx)
|
||||
|
||||
//kotlin
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
|
||||
implementation(libs.mmkv.static)
|
||||
implementation(libs.gson)
|
||||
implementation(libs.rxjava)
|
||||
implementation(libs.rxandroid)
|
||||
implementation(libs.rxpermissions)
|
||||
implementation(libs.toastcompat)
|
||||
implementation(libs.editorkit)
|
||||
implementation(libs.language.base)
|
||||
implementation(libs.language.json)
|
||||
implementation(libs.quickie.bundled)
|
||||
implementation(libs.core)
|
||||
// Background Task Libraries
|
||||
implementation(libs.work.runtime.ktx)
|
||||
implementation(libs.work.multiprocess)
|
||||
|
||||
// Multidex Support
|
||||
implementation(libs.multidex)
|
||||
|
||||
// Testing Libraries
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
testImplementation(libs.org.mockito.mockito.inline)
|
||||
testImplementation(libs.mockito.kotlin)
|
||||
}
|
21
V2rayNG/app/proguard-rules.pro
vendored
21
V2rayNG/app/proguard-rules.pro
vendored
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -13,46 +13,41 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.v2ray.ang.helper;
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
package com.v2ray.ang.helper
|
||||
|
||||
/**
|
||||
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
|
||||
* Interface to listen for a move or dismissal event from a [ItemTouchHelper.Callback].
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public interface ItemTouchHelperAdapter {
|
||||
|
||||
interface ItemTouchHelperAdapter {
|
||||
/**
|
||||
* Called when an item has been dragged far enough to trigger a move. This is called every time
|
||||
* an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
|
||||
* <br/>
|
||||
* Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
|
||||
* an item is shifted, and **not** at the end of a "drop" event.<br></br>
|
||||
* <br></br>
|
||||
* Implementations should call [RecyclerView.Adapter.notifyItemMoved] after
|
||||
* adjusting the underlying data to reflect this move.
|
||||
*
|
||||
* @param fromPosition The start position of the moved item.
|
||||
* @param toPosition Then resolved position of the moved item.
|
||||
* @return True if the item was moved to the new adapter position.
|
||||
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||
* @see RecyclerView.getAdapterPositionFor
|
||||
* @see RecyclerView.ViewHolder.getAdapterPosition
|
||||
*/
|
||||
boolean onItemMove(int fromPosition, int toPosition);
|
||||
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
|
||||
|
||||
|
||||
void onItemMoveCompleted();
|
||||
fun onItemMoveCompleted()
|
||||
|
||||
/**
|
||||
* Called when an item has been dismissed by a swipe.<br/>
|
||||
* <br/>
|
||||
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after
|
||||
* Called when an item has been dismissed by a swipe.<br></br>
|
||||
* <br></br>
|
||||
* Implementations should call [RecyclerView.Adapter.notifyItemRemoved] after
|
||||
* adjusting the underlying data to reflect this removal.
|
||||
*
|
||||
* @param position The position of the item dismissed.
|
||||
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||
* @see RecyclerView.getAdapterPositionFor
|
||||
* @see RecyclerView.ViewHolder.getAdapterPosition
|
||||
*/
|
||||
void onItemDismiss(int position);
|
||||
fun onItemDismiss(position: Int)
|
||||
}
|
|
@ -13,29 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.v2ray.ang.helper
|
||||
|
||||
package com.v2ray.ang.helper;
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
|
||||
/**
|
||||
* Interface to notify an item ViewHolder of relevant callbacks from {@link
|
||||
* ItemTouchHelper.Callback}.
|
||||
* Interface to notify an item ViewHolder of relevant callbacks from [ ].
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public interface ItemTouchHelperViewHolder {
|
||||
|
||||
interface ItemTouchHelperViewHolder {
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
|
||||
* Called when the [ItemTouchHelper] first registers an item as being moved or swiped.
|
||||
* Implementations should update the item view to indicate it's active state.
|
||||
*/
|
||||
void onItemSelected();
|
||||
fun onItemSelected()
|
||||
|
||||
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
|
||||
* Called when the [ItemTouchHelper] has completed the move or swipe, and the active item
|
||||
* state should be cleared.
|
||||
*/
|
||||
void onItemClear();
|
||||
fun onItemClear()
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Paul Burke
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.v2ray.ang.helper;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
|
||||
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
|
||||
* </br/>
|
||||
* Expects the <code>RecyclerView.Adapter</code> to listen for {@link
|
||||
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
|
||||
* {@link ItemTouchHelperViewHolder}.
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||
|
||||
private static final float ALPHA_FULL = 1.0f;
|
||||
private static final float SWIPE_THRESHOLD = 0.25f;
|
||||
private static final long ANIMATION_DURATION = 200;
|
||||
|
||||
private final ItemTouchHelperAdapter mAdapter;
|
||||
private ValueAnimator mReturnAnimator;
|
||||
|
||||
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
|
||||
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
|
||||
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
} else {
|
||||
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) {
|
||||
if (source.getItemViewType() != target.getItemViewType()) {
|
||||
return false;
|
||||
}
|
||||
mAdapter.onItemMove(source.getBindingAdapterPosition(), target.getBindingAdapterPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
// 不执行删除操作,仅返回项目到原位
|
||||
returnViewToOriginalPosition(viewHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
float maxSwipeDistance = viewHolder.itemView.getWidth() * SWIPE_THRESHOLD;
|
||||
float swipeAmount = Math.abs(dX);
|
||||
float direction = Math.signum(dX);
|
||||
|
||||
// 限制最大滑动距离
|
||||
float translationX = Math.min(swipeAmount, maxSwipeDistance) * direction;
|
||||
float alpha = ALPHA_FULL - Math.min(swipeAmount, maxSwipeDistance) / maxSwipeDistance;
|
||||
|
||||
viewHolder.itemView.setTranslationX(translationX);
|
||||
viewHolder.itemView.setAlpha(alpha);
|
||||
|
||||
if (swipeAmount >= maxSwipeDistance && isCurrentlyActive) {
|
||||
returnViewToOriginalPosition(viewHolder);
|
||||
}
|
||||
} else {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
}
|
||||
|
||||
private void returnViewToOriginalPosition(RecyclerView.ViewHolder viewHolder) {
|
||||
if (mReturnAnimator != null && mReturnAnimator.isRunning()) {
|
||||
mReturnAnimator.cancel();
|
||||
}
|
||||
|
||||
mReturnAnimator = ValueAnimator.ofFloat(viewHolder.itemView.getTranslationX(), 0f);
|
||||
mReturnAnimator.addUpdateListener(animation -> {
|
||||
float value = (float) animation.getAnimatedValue();
|
||||
viewHolder.itemView.setTranslationX(value);
|
||||
viewHolder.itemView.setAlpha(1f - Math.abs(value) / (viewHolder.itemView.getWidth() * SWIPE_THRESHOLD));
|
||||
});
|
||||
mReturnAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
mReturnAnimator.setDuration(ANIMATION_DURATION);
|
||||
mReturnAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
|
||||
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemSelected();
|
||||
}
|
||||
}
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
viewHolder.itemView.setAlpha(ALPHA_FULL);
|
||||
if (viewHolder instanceof ItemTouchHelperViewHolder) {
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemClear();
|
||||
}
|
||||
mAdapter.onItemMoveCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
return 1.1f; // 设置一个大于1的值,确保不会触发默认的滑动删除操作
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSwipeEscapeVelocity(float defaultValue) {
|
||||
return defaultValue * 10; // 增加滑动逃逸速度,使得更难触发滑动
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Paul Burke
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.v2ray.ang.helper
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener
|
||||
import android.graphics.Canvas
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
|
||||
/**
|
||||
* An implementation of [ItemTouchHelper.Callback] that enables basic drag & drop and
|
||||
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br></br>
|
||||
*
|
||||
* Expects the `RecyclerView.Adapter` to listen for [ ] callbacks and the `RecyclerView.ViewHolder` to implement
|
||||
* [ItemTouchHelperViewHolder].
|
||||
*
|
||||
* @author Paul Burke (ipaulpro)
|
||||
*/
|
||||
class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() {
|
||||
private var mReturnAnimator: ValueAnimator? = null
|
||||
|
||||
override fun isLongPressDragEnabled(): Boolean = true
|
||||
|
||||
override fun isItemViewSwipeEnabled(): Boolean = true
|
||||
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
val dragFlags: Int
|
||||
val swipeFlags: Int
|
||||
if (recyclerView.layoutManager is GridLayoutManager) {
|
||||
dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||
swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END
|
||||
} else {
|
||||
dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||
swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END
|
||||
}
|
||||
return makeMovementFlags(dragFlags, swipeFlags)
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
source: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return if (source.itemViewType != target.itemViewType) {
|
||||
false
|
||||
} else {
|
||||
mAdapter.onItemMove(source.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
// Do not delete; simply return item to original position
|
||||
returnViewToOriginalPosition(viewHolder)
|
||||
}
|
||||
|
||||
override fun onChildDraw(
|
||||
c: Canvas, recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean
|
||||
) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
|
||||
val maxSwipeDistance = viewHolder.itemView.width * SWIPE_THRESHOLD
|
||||
val swipeAmount = abs(dX)
|
||||
val direction = sign(dX)
|
||||
|
||||
// Limit maximum swipe distance
|
||||
val translationX = min(swipeAmount, maxSwipeDistance) * direction
|
||||
val alpha = ALPHA_FULL - min(swipeAmount, maxSwipeDistance) / maxSwipeDistance
|
||||
|
||||
viewHolder.itemView.translationX = translationX
|
||||
viewHolder.itemView.alpha = alpha
|
||||
|
||||
if (swipeAmount >= maxSwipeDistance && isCurrentlyActive) {
|
||||
returnViewToOriginalPosition(viewHolder)
|
||||
}
|
||||
} else {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnViewToOriginalPosition(viewHolder: RecyclerView.ViewHolder) {
|
||||
mReturnAnimator?.takeIf { it.isRunning }?.cancel()
|
||||
|
||||
mReturnAnimator = ValueAnimator.ofFloat(viewHolder.itemView.translationX, 0f).apply {
|
||||
addUpdateListener { animation ->
|
||||
val value = animation.animatedValue as Float
|
||||
viewHolder.itemView.translationX = value
|
||||
viewHolder.itemView.alpha = (1f - abs(value) / (viewHolder.itemView.width * SWIPE_THRESHOLD))
|
||||
}
|
||||
interpolator = DecelerateInterpolator()
|
||||
duration = ANIMATION_DURATION
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE && viewHolder is ItemTouchHelperViewHolder) {
|
||||
viewHolder.onItemSelected()
|
||||
}
|
||||
super.onSelectedChanged(viewHolder, actionState)
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.itemView.alpha = ALPHA_FULL
|
||||
if (viewHolder is ItemTouchHelperViewHolder) {
|
||||
viewHolder.onItemClear()
|
||||
}
|
||||
mAdapter.onItemMoveCompleted()
|
||||
}
|
||||
|
||||
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
|
||||
return 1.1f // Set a value greater than 1 to prevent default swipe delete
|
||||
}
|
||||
|
||||
override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
|
||||
return defaultValue * 10 // Increase swipe escape velocity to make swipe harder to trigger
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ALPHA_FULL = 1.0f
|
||||
private const val SWIPE_THRESHOLD = 0.25f
|
||||
private const val ANIMATION_DURATION: Long = 200
|
||||
}
|
||||
}
|
|
@ -13,13 +13,11 @@ object AppManagerUtil {
|
|||
val apps = ArrayList<AppInfo>()
|
||||
|
||||
for (pkg in packages) {
|
||||
//if (!pkg.hasInternetPermission && pkg.packageName != "android") continue
|
||||
|
||||
val applicationInfo = pkg.applicationInfo
|
||||
val applicationInfo = pkg.applicationInfo ?: continue
|
||||
|
||||
val appName = applicationInfo.loadLabel(packageManager).toString()
|
||||
val appIcon = applicationInfo.loadIcon(packageManager)
|
||||
val isSystemApp = applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM > 0
|
||||
val appIcon = applicationInfo.loadIcon(packageManager) ?: continue
|
||||
val isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) > 0
|
||||
|
||||
val appInfo = AppInfo(appName, pkg.packageName, appIcon, isSystemApp, 0)
|
||||
apps.add(appInfo)
|
47
V2rayNG/app/src/test/java/com/v2ray/ang/AngUnitTest.kt
Normal file
47
V2rayNG/app/src/test/java/com/v2ray/ang/AngUnitTest.kt
Normal file
|
@ -0,0 +1,47 @@
|
|||
package com.v2ray.ang
|
||||
|
||||
import com.v2ray.ang.util.Utils
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class AngUnitTest {
|
||||
|
||||
@Test
|
||||
fun test_parseInt() {
|
||||
assertEquals(Utils.parseInt("1234"), 1234)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_isIpAddress() {
|
||||
assertFalse(Utils.isIpAddress("114.113.112.266"))
|
||||
assertFalse(Utils.isIpAddress("666.666.666.666"))
|
||||
assertFalse(Utils.isIpAddress("256.0.0.0"))
|
||||
assertFalse(Utils.isIpAddress("::ffff:127.0.0.0.1"))
|
||||
assertFalse(Utils.isIpAddress("baidu.com"))
|
||||
assertFalse(Utils.isIpAddress(""))
|
||||
|
||||
assertTrue(Utils.isIpAddress("127.0.0.1"))
|
||||
assertTrue(Utils.isIpAddress("127.0.0.1:80"))
|
||||
assertTrue(Utils.isIpAddress("0.0.0.0/0"))
|
||||
assertTrue(Utils.isIpAddress("::1"))
|
||||
assertTrue(Utils.isIpAddress("[::1]:80"))
|
||||
assertTrue(Utils.isIpAddress("2605:2700:0:3::4713:93e3"))
|
||||
assertTrue(Utils.isIpAddress("[2605:2700:0:3::4713:93e3]:80"))
|
||||
assertTrue(Utils.isIpAddress("::ffff:192.168.173.22"))
|
||||
assertTrue(Utils.isIpAddress("[::ffff:192.168.173.22]:80"))
|
||||
assertTrue(Utils.isIpAddress("1::"))
|
||||
assertTrue(Utils.isIpAddress("::"))
|
||||
assertTrue(Utils.isIpAddress("::/0"))
|
||||
assertTrue(Utils.isIpAddress("10.24.56.0/24"))
|
||||
assertTrue(Utils.isIpAddress("2001:4321::1"))
|
||||
assertTrue(Utils.isIpAddress("240e:1234:abcd:12::6666"))
|
||||
assertTrue(Utils.isIpAddress("240e:1234:abcd:12::/64"))
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.v2ray.ang;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
import com.v2ray.ang.util.Utils
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class UtilTest {
|
||||
|
||||
@Test
|
||||
fun test_parseInt() {
|
||||
assertEquals(Utils.parseInt("1234"), 1234)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_isIpAddress() {
|
||||
assertFalse(Utils.isIpAddress("114.113.112.266"))
|
||||
assertFalse(Utils.isIpAddress("666.666.666.666"))
|
||||
assertFalse(Utils.isIpAddress("256.0.0.0"))
|
||||
assertFalse(Utils.isIpAddress("::ffff:127.0.0.0.1"))
|
||||
assertFalse(Utils.isIpAddress("baidu.com"))
|
||||
assertFalse(Utils.isIpAddress(""))
|
||||
|
||||
assertTrue(Utils.isIpAddress("127.0.0.1"))
|
||||
assertTrue(Utils.isIpAddress("127.0.0.1:80"))
|
||||
assertTrue(Utils.isIpAddress("0.0.0.0/0"))
|
||||
assertTrue(Utils.isIpAddress("::1"))
|
||||
assertTrue(Utils.isIpAddress("[::1]:80"))
|
||||
assertTrue(Utils.isIpAddress("2605:2700:0:3::4713:93e3"))
|
||||
assertTrue(Utils.isIpAddress("[2605:2700:0:3::4713:93e3]:80"))
|
||||
assertTrue(Utils.isIpAddress("::ffff:192.168.173.22"))
|
||||
assertTrue(Utils.isIpAddress("[::ffff:192.168.173.22]:80"))
|
||||
assertTrue(Utils.isIpAddress("1::"))
|
||||
assertTrue(Utils.isIpAddress("::"))
|
||||
assertTrue(Utils.isIpAddress("::/0"))
|
||||
assertTrue(Utils.isIpAddress("10.24.56.0/24"))
|
||||
assertTrue(Utils.isIpAddress("2001:4321::1"))
|
||||
assertTrue(Utils.isIpAddress("240e:1234:abcd:12::6666"))
|
||||
assertTrue(Utils.isIpAddress("240e:1234:abcd:12::/64"))
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun test_fmtHysteria2Parse() {
|
||||
// val url2 = "hysteria2://password2@127.0.0.1:443?obfs=salamander&obfs-password=obfs2&insecure=0#Hy22"
|
||||
// var result2 = Hysteria2Fmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// assertTrue(result2?.obfsPassword == "obfs2")
|
||||
// assertTrue(result2?.security == "tls")
|
||||
//
|
||||
// var url22 = Hysteria2Fmt.toUri(result2!!)
|
||||
// assertTrue(url22.contains("obfs2"))
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtSsParse() {
|
||||
// val url2 = "ss://aa:bb@127.0.0.1:10000#sss"
|
||||
// var result2 = ShadowsocksFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
//
|
||||
// var result = ShadowsocksFmt.parse("ss://YWVzLTI1Ni1nY206cGFzc3dvcmQy@127.0.0.1:10000#sss")
|
||||
// assertTrue(result != null)
|
||||
// assertTrue(result?.server == "127.0.0.1")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtSocksParse() {
|
||||
// val url2 = "socks://Og%3D%3D@127.0.0.1:1000#socks2"
|
||||
// var result2 = SocksFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// var url22 = SocksFmt.toUri(result2!!)
|
||||
// assertTrue(url2.contains(url22))
|
||||
//
|
||||
// var result = SocksFmt.parse("socks://dXNlcjpwYXNz@127.0.0.1:1000#socks2")
|
||||
// assertTrue(result != null)
|
||||
// assertTrue(result?.server == "127.0.0.1")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtTrojanParse() {
|
||||
// val url2 = "trojan://password2@127.0.0.1:443?flow=xtls-rprx-vision&security=tls&type=tcp&headerType=none#Trojan"
|
||||
// var result2 = TrojanFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// assertTrue(result2?.flow == "xtls-rprx-vision")
|
||||
//
|
||||
// val url = "trojan://password2@127.0.0.1:443#Trojan"
|
||||
// var result = TrojanFmt.parse(url)
|
||||
// assertTrue(result != null)
|
||||
// assertTrue(result?.server == "127.0.0.1")
|
||||
// assertTrue(result?.security == "tls")
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtVlessParse() {
|
||||
// val url2 =
|
||||
// "vless://cae1dc39-0547-4b1d-9e7a-01132c7ae3a7@127.0.0.1:443?encryption=none&flow=xtls-rprx-vision&security=reality&sni=sni2&fp=chrome&pbk=publickkey&sid=123456&spx=%2F&type=ws&host=host2&path=path2#VLESS"
|
||||
// var result2 = VlessFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// assertTrue(result2?.flow == "xtls-rprx-vision")
|
||||
//
|
||||
//
|
||||
// var url22 = VlessFmt.toUri(result2!!)
|
||||
// assertTrue(url22.contains("xtls-rprx-vision"))
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtVmessParse() {
|
||||
// val url2 =
|
||||
// "vmess://ew0KICAidiI6ICIyIiwNCiAgInBzIjogIlZtZXNzIiwNCiAgImFkZCI6ICIxMjcuMC4wLjEiLA0KICAicG9ydCI6ICIxMDAwMCIsDQogICJpZCI6ICJlYmI5MWM5OS1lZjA3LTRmZjUtOThhYS01OTAyYWI0ZDAyODYiLA0KICAiYWlkIjogIjEyMyIsDQogICJzY3kiOiAiYWVzLTEyOC1nY20iLA0KICAibmV0IjogInRjcCIsDQogICJ0eXBlIjogIm5vbmUiLA0KICAiaG9zdCI6ICJob3N0MiIsDQogICJwYXRoIjogInBhdGgyIiwNCiAgInRscyI6ICIiLA0KICAic25pIjogIiIsDQogICJhbHBuIjogIiINCn0="
|
||||
// var result2 = VmessFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// assertTrue(result2?.method == "aes-128-gcm")
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Test
|
||||
// fun test_fmtWireguardParse() {
|
||||
// val url2 = "wireguard://privatekey2@127.0.0.1:2000?publickey=publickey2&reserved=2%2C2%2C3&address=127.0.0.127&mtu=1250#WGG"
|
||||
// var result2 = WireguardFmt.parse(url2)
|
||||
// assertTrue(result2 != null)
|
||||
// assertTrue(result2?.server == "127.0.0.1")
|
||||
// assertTrue(result2?.publicKey == "publickey2")
|
||||
// assertTrue(result2?.localAddress == "127.0.0.127")
|
||||
//
|
||||
//
|
||||
// var url22 = WireguardFmt.toUri(result2!!)
|
||||
// assertTrue(url22.contains("publickey2"))
|
||||
// }
|
||||
}
|
||||
|
|
@ -2,5 +2,5 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.android.kotlin) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
}
|
|
@ -1,6 +1,24 @@
|
|||
kotlin.incremental=true
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
kotlin.incremental=true
|
|
@ -1,71 +1,63 @@
|
|||
[versions]
|
||||
activityKtx = "1.9.3"
|
||||
appcompat = "1.7.0"
|
||||
cardview = "1.0.0"
|
||||
constraintlayout = "2.2.0"
|
||||
core = "3.5.3"
|
||||
editorkit = "2.9.0"
|
||||
flexbox = "3.0.0"
|
||||
fragmentKtx = "1.8.5"
|
||||
gson = "2.11.0"
|
||||
agp = "8.7.2"
|
||||
kotlin = "2.0.21"
|
||||
coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
kotlinReflect = "2.0.21"
|
||||
kotlinxCoroutinesCore = "1.9.0"
|
||||
legacySupportV4 = "1.0.0"
|
||||
lifecycleViewmodelKtx = "2.8.7"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
appcompat = "1.7.0"
|
||||
material = "1.12.0"
|
||||
activity = "1.9.3"
|
||||
constraintlayout = "2.2.0"
|
||||
mmkvStatic = "1.3.9"
|
||||
multidex = "2.0.1"
|
||||
preferenceKtx = "1.2.1"
|
||||
quickieBundled = "1.10.0"
|
||||
recyclerview = "1.3.2"
|
||||
rxandroid = "3.0.2"
|
||||
gson = "2.11.0"
|
||||
rxjava = "3.1.9"
|
||||
rxandroid = "3.0.2"
|
||||
rxpermissions = "0.12"
|
||||
toastcompat = "1.1.0"
|
||||
viewpager2 = "1.1.0"
|
||||
workRuntimeKtx = "2.9.1"
|
||||
androidGradlePlugin = "8.7.2"
|
||||
androidKotlinPlugin = "2.0.21"
|
||||
editorkit = "2.9.0"
|
||||
quickieBundled = "1.10.0"
|
||||
core = "3.5.3"
|
||||
workRuntimeKtx = "2.10.0"
|
||||
lifecycleViewmodelKtx = "2.8.7"
|
||||
multidex = "2.0.1"
|
||||
mockitoMockitoInline = "4.0.0"
|
||||
|
||||
flexbox = "3.0.0"
|
||||
preferenceKtx = "1.2.1"
|
||||
recyclerview = "1.3.2"
|
||||
[libraries]
|
||||
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
cardview = { module = "androidx.cardview:cardview", version.ref = "cardview" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
|
||||
core = { module = "com.google.zxing:core", version.ref = "core" }
|
||||
editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" }
|
||||
flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "flexbox" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
junit = { module = "junit:junit", version.ref = "junit" }
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesCore" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||
language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" }
|
||||
language-json = { module = "com.blacksquircle.ui:language-json", version.ref = "editorkit" }
|
||||
legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4" }
|
||||
lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
|
||||
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
|
||||
quickie-bundled = { module = "io.github.g00fy2.quickie:quickie-bundled", version.ref = "quickieBundled" }
|
||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||
preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtx" }
|
||||
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" }
|
||||
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
|
||||
rxpermissions = { module = "com.github.tbruyelle:rxpermissions", version.ref = "rxpermissions" }
|
||||
toastcompat = { module = "me.drakeet.support:toastcompat", version.ref = "toastcompat" }
|
||||
viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }
|
||||
work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "workRuntimeKtx" }
|
||||
editorkit = { module = "com.blacksquircle.ui:editorkit", version.ref = "editorkit" }
|
||||
language-base = { module = "com.blacksquircle.ui:language-base", version.ref = "editorkit" }
|
||||
language-json = { module = "com.blacksquircle.ui:language-json", version.ref = "editorkit" }
|
||||
quickie-bundled = { module = "io.github.g00fy2.quickie:quickie-bundled", version.ref = "quickieBundled" }
|
||||
core = { module = "com.google.zxing:core", version.ref = "core" }
|
||||
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
||||
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoMockitoInline" }
|
||||
work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "workRuntimeKtx" }
|
||||
lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
|
||||
org-mockito-mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoMockitoInline" }
|
||||
|
||||
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoMockitoInline" }
|
||||
flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "flexbox" }
|
||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||
preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtx" }
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
|
||||
android-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "androidKotlinPlugin" }
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
|
|
BIN
V2rayNG/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
V2rayNG/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue