commit  loacal
更新代码4
This commit is contained in:
level 2025-07-04 17:00:18 +08:00
parent b2fd37a68e
commit 51dede7fb2
12 changed files with 110 additions and 296 deletions

View File

@ -36,7 +36,6 @@ import com.yingmao.clash.act.NewMainActivity.Companion.clashBinder
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.NewMainActivity.Companion.profileBinder
import com.yingmao.clash.act.NewMainActivity.Companion.subUrl
import com.yingmao.clash.act.UIMainActivity.Companion.isSubed
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.conf.BZYConstants

View File

@ -22,7 +22,6 @@ import com.google.gson.Gson
import com.google.gson.JsonArray
import com.tencent.mmkv.MMKV
import com.yingmao.clash.act.ChangeNodeActivity.Companion
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.base.BaseActivity
@ -145,7 +144,6 @@ class MainActivity : BaseActivity() {
com.cncat.vpn.common.log.Log.d("new setactive name " + profile.name)
pm.setActive(profile)
newUuid = profile.uuid.toString();
isSubed = true
var selected_name =
MMKV.defaultMMKV()
.decodeString(BZYConstants.NODE_SELECTED_NAME_KEY, "")

View File

@ -38,7 +38,8 @@ import com.hjq.permissions.XXPermissions
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.isSubbed
import com.yingmao.clash.act.NewMainActivity.Companion.newMainActGlo
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.NewMainActivity.Companion.profileBinder
import com.yingmao.clash.act.NewMainActivity.Companion.subUrl
@ -333,7 +334,7 @@ class SettingActivity : BaseActivity() {
delay(1000)
if (subUrl?.isNotEmpty() == true) {
startTimer()
setSub2(subUrl!!,pm)
NewMainActivity.setSub2(subUrl!!)
}
}
}
@ -604,58 +605,6 @@ class SettingActivity : BaseActivity() {
tv_proxy_model.text = resources.getString(R.string.RoutingMode)
}
}
suspend fun setSub2(url: String, pm: IProfileManager) {
Log.d("setSub2 2")
pm.queryAll().forEach { itemPro ->
if (itemPro.name != Conf.getClashConfName(MainApplication.getApp())) {
Log.d("setSub2 delete ==>${itemPro.name}")
pm.delete(itemPro.uuid)
}
}
val pro = pm.queryActive()
Log.d("setSub2 4:::"+pro)
pro?.let {
var actname = it.name
Log.d("new main actname==>" + actname)
// Log.d("setSub2 pro 已存在 ==>" + Gson().toJson(pro))
pm.setActive(it)
newUuid = pro.uuid.toString();
isSubed = true
Log.d("setSub2 setActive end")
finishInit()
} ?: run {
var uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(MainApplication.getApp())
)
delay(500)
pm.queryByUUID(uuid)?.let { qPro ->
pm.setActive(qPro)
pm.patch(
qPro.uuid, qPro.name,
// "https://mojie0201.xn--8stx8olrwkucjq3b.com/api/v1/client/subscribe?token=169110d2674c49604b7e5b5cea341579",
// "https://www.otcopusapp.cc/lx3af288h5i8pz380/api/v1/client/subscribe?token=d6bc98c7ea53099fa5bd1a3ea415ebb6",
// "https://reg.bazhuayujiasu.cc/api/v1/client/subscribe?token=7f0f025ab341bcec4822d2c90aadf8b0",
url, 1800000L
)
// coroutineScope {
pm.commit(qPro.uuid) { status ->
android.util.Log.d(
Conf.TAG,
"new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
)
}
newUuid = qPro.uuid.toString();
isSubed = true
// }
}
finishInit()
}
}
private fun finishInit() {
timerJob?.cancel()

View File

@ -566,99 +566,6 @@ class SplashActivity : BaseActivity() {
splashAtyVM.registerByEncryption(params)
}
}
// private fun initSub() {
// var yaml = context?.getString(com.cncat.vpn.service.R.string.configuration_yaml)
// Log.d(Conf.TAG, "initSub: $yaml")
//
// accelerateViewModel.statusData.observe(viewLifecycleOwner) {
// when (it) {
// is HttpStatus.Loading -> {
//
// Log.d(Conf.TAG, "正在获取节点信息...")
// }
//
// is HttpStatus.Success -> {
//
// val data = it.rsp as SubscriptionRsp
// if (data.code == "1000") {
//
// } else {
//// dismissDialog()
// RxToast.error("获取节点失败:${data.msg}")
// }
//
// data.data?.let { url ->
// Log.e("Data", "机场URL=${url}")
// if (url.isNotEmpty()) {
//// lifecycleScope.launch {
////
//// }
//// sendMessage(1, url)
// setSub(url)
//
// }
// }
//
//// }
// }
//
// is HttpStatus.Failure -> {
// if (it.reqType == HttpReqType.GET_AIRNODES) {
//// dismissDialog()
//// showRetryDialog()
// Logger.e("获取节点异常:${it.msg}")
// }
// }
// }
// }
// }
// private fun setSub(url: String) {
// com.cncat.vpn.common.log.Log.d("setSub....")
//
// lifecycleScope.launch (Dispatchers.IO) {
// com.cncat.vpn.common.log.Log.d("setSub....11")
// withProfile {
// com.cncat.vpn.common.log.Log.d("setSub....2")
// val pro = queryAll().find {
// it.name == "八爪猫"
// }
//
// if (pro == null) {
// com.cncat.vpn.common.log.Log.d("setSub.... pro is null")
// var uuid: UUID = create(Profile.Type.Url, "八爪猫")
// delay(500)
// queryByUUID(uuid)?.let { qPro ->
// setActive(qPro)
// patch(
// qPro.uuid, qPro.name,
//// "https://mojie0201.xn--8stx8olrwkucjq3b.com/api/v1/client/subscribe?token=169110d2674c49604b7e5b5cea341579",
//// "https://www.otcopusapp.cc/lx3af288h5i8pz380/api/v1/client/subscribe?token=d6bc98c7ea53099fa5bd1a3ea415ebb6",
// url, 1800000L
// )
//// coroutineScope {
// commit(qPro.uuid) { status ->
// Log.d(
// Conf.TAG,
// "new updateStatus: " + Gson().toJson(status.args) + ", pro==>" + status.progress
// )
// }
// newUuid = qPro.uuid.toString();
// isSubed = true
//// }
// }
// } else {
// Log.d(Conf.TAG, "pro 已存在 ==>" + Gson().toJson(pro))
// setActive(pro)
//// update(pro.uuid)
// newUuid = pro.uuid.toString();
// isSubed = true
// }
// profile = pro;
// }
// }
//
// }
@SuppressLint("ClickableViewAccessibility")
override fun httpSuccess(httpStatus: HttpStatus.Success) {
when (httpStatus.reqType) {
@ -807,58 +714,22 @@ class SplashActivity : BaseActivity() {
when (httpStatus.reqType) {
HttpReqType.REGISTER -> {
rlPb.visibility = View.GONE
pb_web.visibility = View.INVISIBLE
pb_web?.visibility = View.INVISIBLE
com.cncat.vpn.common.log.Log.d("httpFailure HttpReqType.REGISTER")
try {
RxToast.error("检测到网络异常,请尝试切换其他地址.")
if (dialogDoMainChoose == null) {
dialogDoMainChoose = DialogDoMainChoose(this@SplashActivity)
if (super.switchServer2()) {//切换服务器并直接重试
doRegisterByEncryption()
} else {
MaterialDialog(this@SplashActivity).show {
message(text = initErrMsg)
positiveButton(R.string.retry) {
doRegisterByEncryption()
}
negativeButton(R.string.cancel) {
it.dismiss()
finish()
}
}
if (dialogDoMainChoose?.isShowing == false) {
dialogDoMainChoose?.setOnTryButtonListener(object :
DialogDoMainChoose.OnTryButtonListener {
override fun tryButton() {
var currentHost = MMKV.defaultMMKV()
.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
// com.cncat.vpn.common.log.Log.d("tryButton currentHost==>${currentHost}")
doRegisterByEncryption()
}
override fun cancel() {
com.cncat.vpn.common.log.Log.d("tryButton REGISTER cancel")
dialogDoMainChoose?.dismiss()
dialogDoMainChoose = null
}
})
dialogDoMainChoose?.setOnListItemListener(object :
DialogDoMainChoose.OnListItemListener {
override fun listItemClick() {
doRegisterByEncryption()
dialogDoMainChoose?.dismiss()
}
})
dialogDoMainChoose?.show()
}
} catch (e: Exception) {
e.printStackTrace()
}
// if (super.switchServer()) {//切换服务器并直接重试
//// doRegister()
// doRegisterByEncryption()
// } else {
// MaterialDialog(this@SplashActivity).show {
// message(text = initErrMsg)
// positiveButton(R.string.retry) {
//// doRegister()
// doRegisterByEncryption()
// }
// negativeButton(R.string.cancel) {
// it.dismiss()
// finish()
// }
// }
// }
}
HttpReqType.LOGIN -> {
@ -884,48 +755,16 @@ class SplashActivity : BaseActivity() {
com.cncat.vpn.common.log.Log.d("httpFailure HttpReqType.CHECK_LOGIN")
try {
RxToast.error("检测到网络异常,请尝试切换其他地址.")
SplashHelper(splashAtyVM).getHostWithFallback { success ->
MainScope().launch {
if (success) {
if (dialogDoMainChoose == null) {
dialogDoMainChoose = DialogDoMainChoose(this@SplashActivity)
}
if (dialogDoMainChoose?.isShowing == false) {
dialogDoMainChoose?.setOnTryButtonListener(object :
DialogDoMainChoose.OnTryButtonListener {
override fun tryButton() {
splashAtyVM.checkLogin()
}
override fun cancel() {
com.cncat.vpn.common.log.Log.d("tryButton CHECK_LOGIN cancel")
dialogDoMainChoose?.dismiss()
}
})
dialogDoMainChoose?.setOnListItemListener(object :
DialogDoMainChoose.OnListItemListener {
override fun listItemClick() {
splashAtyVM.checkLogin()
}
})
dialogDoMainChoose?.show()
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
toLoginPage()
}
// if (super.switchServer()) {
// splashAtyVM.checkLogin()
// } else {
// toLoginPage()
// }
if (super.switchServer2()) {
splashAtyVM.checkLogin()
} else {
toLoginPage()
}
}
else -> {}

View File

@ -111,7 +111,6 @@ class UIMainActivity : BaseActivity() {
var profileBinder: IProfileManager? = null
var isUpload: Boolean = false
var hostsMem: ArrayList<String> = ArrayList()
var isSubed = false
var newMainActGlo: Boolean = false
}

View File

@ -1,20 +1,33 @@
package com.yingmao.clash.base
import android.os.Build
import android.text.TextUtils
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.cncat.vpn.common.log.Log
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.net.http.HttpApi
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.HttpStatusDownLoad
import com.yingmao.clash.net.http.NetService
import com.yingmao.clash.net.http.entity.BaseRsp
import com.yingmao.clash.util.AESUtil
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONArray
import org.json.JSONObject
import java.io.PrintWriter
import java.io.StringWriter
open class BaseViewMode : ViewModel() {
@ -24,9 +37,13 @@ open class BaseViewMode : ViewModel() {
fun doAsync(reqType: HttpReqType, doInAsync: suspend () -> Any) {
viewModelScope.launch(
CoroutineExceptionHandler { _,
exception ->
Log.e("BaseViewMode_doAsync:${exception.toString()}", exception)
CoroutineExceptionHandler {
_,
exception,
->
Log.e("BaseViewMode_doAsync:${exception.localizedMessage}", exception)
logException(exception)
statusData.value =
if (exception.message.isNullOrBlank()) HttpStatus.Failure(
reqType,
@ -46,7 +63,7 @@ open class BaseViewMode : ViewModel() {
Log.i("请求结果:$rsp")
statusData.value = HttpStatus.Success(reqType, rsp)
} catch (e: Exception) {
Log.e( "网络请求异常",e)
Log.e("网络请求异常", e)
statusData.value = HttpStatus.Failure(reqType, e.toString())
}
}
@ -250,36 +267,53 @@ open class BaseViewMode : ViewModel() {
}
}
// fun <T>doAsyncs(doInAsyncs :Map<T,suspend () -> T>) {
// viewModelScope.launch (
// CoroutineExceptionHandler(){
// _,
// exception ->
// statusData.value = if (exception.message.isNullOrBlank()) HttpStatus.Failure("登录异常") else HttpStatus.Failure("登录异常:${exception.message}")
//
// })
// {
// statusData.value = HttpStatus.Loading
// doInAsyncs.forEach {
// it.key
// val result =async{
// it.value
// }
// val rsp = result.await()
// }
// val result = async { doInAsync()}
// val rsp :T = result.await()
// tData.value = rsp
// statusData.value = HttpStatus.Success
// }
// }
fun logException(e: Throwable) {
var logSwitch = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_VPN_LogSwitch, "")
if ("on" == logSwitch) {
val log = StringWriter().also { e.printStackTrace(PrintWriter(it)) }.toString()
Log.d("logException==>${log}")
val username =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
var json = JSONObject()
val androidVersion = Build.VERSION.RELEASE // 系统版本号,例如 "13"
val sdkInt = Build.VERSION.SDK_INT // SDK 级别,例如 33
val manufacturer = Build.MANUFACTURER // 厂商,例如 "Xiaomi"
val model = Build.MODEL // 机型,例如 "MI 11"
val brand = Build.BRAND // 品牌,例如 "Xiaomi"
val device = Build.DEVICE // 设备代号,例如 "venus"
val hardware = Build.HARDWARE // 硬件名称,例如 "qcom"
// inner class Factory(private val dataMap: HashMap<String, Any>):ViewModelProvider.Factory{
//
// override fun <T : ViewModel?> create(modelClass: Class<T>): T {
//
// return BaseViewMode2(dataMap) as T
// }
//
// }
// val nowTime = SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒E", Locale.getDefault())
var ja_streams = JSONArray()
var jo_streams_item = JSONObject()
var jo_stream = JSONObject()
var ja_values = JSONArray()
jo_stream.put("phoneNumber", username)
jo_stream.put("appVersion", BuildConfig.VERSION_NAME)
jo_stream.put("androidVersion", androidVersion)
jo_stream.put("sdkInt", sdkInt)
jo_stream.put("manufacturer", manufacturer)
jo_stream.put("model", model)
jo_stream.put("brand", brand)
jo_stream.put("device", device)
jo_stream.put("hardware", hardware)
ja_values.put(0, System.currentTimeMillis().toString() + "000000")
//错误信息
ja_values.put(1, log)
jo_streams_item.put("stream", jo_stream)
var ja_values_item = JSONArray()
ja_values_item.put(0, ja_values)
jo_streams_item.put("values", ja_values_item)
ja_streams.put(0, jo_streams_item)
json.put("streams", ja_streams)
Log.d("json==>${json}")
val httpApi = NetService.instances.createHttps(HttpApi::class.java)
val requestBody: RequestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
doAsync2(HttpReqType.APPSHAREINFO) {
httpApi.postErrLog(requestBody)
}
}
}
}

View File

@ -121,6 +121,7 @@ class BZYConstants {
const val CONNECTID = "connectId"
const val is_splash_first = "is_splash_first"
const val MMKV_KEY_Switch_Finsh_Set ="MMKV_KEY_Switch_Finsh_Set"
const val MMKV_KEY_VPN_LogSwitch = "logSwitch"
}
}

View File

@ -18,7 +18,6 @@ import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.cncat.vpn.common.constants.Intents
@ -28,11 +27,9 @@ import com.cncat.vpn.core.model.TunnelState
import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV
import com.yingmao.clash.R
import com.yingmao.clash.act.ChangeNodeActivity
import com.yingmao.clash.act.NewMainActivity
import com.yingmao.clash.act.NewMainActivity.Companion.clashBinder
import com.yingmao.clash.act.NewMainActivity.Companion.isSubbed
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.PageInfo
import com.yingmao.clash.conf.BZYConstants
@ -45,7 +42,6 @@ import com.yingmao.clash.net.http.entity.AppStartupStatus
import com.yingmao.clash.net.http.entity.ExpensesRecordRsp
import com.yingmao.clash.service.ConnectionService
import com.yingmao.clash.util.ActivityResultLifecycle
import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.util.UiStore
import com.yingmao.clash.util.Utils
import com.yingmao.clash.util.startClashService
@ -59,11 +55,10 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@ -236,14 +231,14 @@ open class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dis
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubed = true
isSubbed = true
} ?: run {
//为空的情况下选择第一个
pm.queryAll().forEach { profile ->
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubed = true
isSubbed = true
return@forEach
}
}

View File

@ -45,7 +45,7 @@ import com.yingmao.clash.act.ConnectionModeActivity
import com.yingmao.clash.act.LoginActivity
import com.yingmao.clash.act.UIMainActivity
import com.yingmao.clash.act.UIMainActivity.Companion.clashBinder
import com.yingmao.clash.act.UIMainActivity.Companion.isSubed
import com.yingmao.clash.act.UIMainActivity.Companion.isSubbed
import com.yingmao.clash.act.UIMainActivity.Companion.newMainActGlo
import com.yingmao.clash.act.UIMainActivity.Companion.newUuid
import com.yingmao.clash.act.UIMainActivity.Companion.profileBinder
@ -520,14 +520,14 @@ class HomeFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatcher
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubed = true
isSubbed = true
} ?: run {
//为空的情况下选择第一个
pm.queryAll().forEach { profile ->
pm.setActive(profile)
newUuid = profile.uuid.toString()
isSubed = true
isSubbed = true
return@forEach
}
}

View File

@ -54,10 +54,10 @@ import com.yingmao.clash.act.InstallApplicationActivity
import com.yingmao.clash.act.LoginActivity
import com.yingmao.clash.act.LossPreventionActivity
import com.yingmao.clash.act.NewMainActivity
import com.yingmao.clash.act.NewMainActivity.Companion.isSubbed
import com.yingmao.clash.act.ShareActivity
import com.yingmao.clash.act.SplashActivity
import com.yingmao.clash.act.SplashHelper
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.NewMainActivity.Companion.profileBinder
import com.yingmao.clash.act.NewMainActivity.Companion.subUrl
@ -824,7 +824,7 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
delay(500)
pm.update(it.uuid)
newUuid = pro.uuid.toString()
isSubed = true
isSubbed = true
} ?: run {
val uuid: UUID = pm.create(
Profile.Type.Url, Conf.getClashConfName(requireActivity().applicationContext)
@ -843,7 +843,7 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
)
}
newUuid = qPro.uuid.toString()
isSubed = true
isSubbed = true
// }
}
}
@ -903,7 +903,7 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
Log.d("new main actname==>" + actName)
pm.setActive(it)
newUuid = pro.uuid.toString();
isSubed = true
isSubbed = true
Log.d("setSub2 setActive end")
finishInit()
} ?: run {
@ -924,7 +924,7 @@ class SettingFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatc
)
}
newUuid = qPro.uuid.toString();
isSubed = true
isSubbed = true
// }
}
finishInit()

View File

@ -93,10 +93,10 @@ class NetService private constructor() : INetService {
// .sslSocketFactory(getSSLSocketFactory(), CustomTrustManager())//信任所有证书
.sslSocketFactory(getSSLSocketFactory(), trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(getHostnameVerifier()!!)
if (BuildConfig.DEBUG) {
builder.addInterceptor(loggingInterceptor)
}
// builder.addInterceptor(loggingInterceptor)
// if (BuildConfig.DEBUG) {
// builder.addInterceptor(loggingInterceptor)
// }
builder.addInterceptor(loggingInterceptor)
return builder.build()
}

View File

@ -157,7 +157,7 @@ subprojects {
)
)
}
create("2222") {
create("22222") {
isDefault = true
dimension = flavorDimensionList[0]
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
@ -166,9 +166,9 @@ subprojects {
buildConfigField("boolean", "is_tz", "false")
buildConfigField("boolean", "is_yunti", "false")
buildConfigField("String", "applicationId", "\"com.jsq.wufu\"")
buildConfigField("String", "service_url", "\"https://api.yijianlianjsq.com/\"")
buildConfigField("String", "service_url", "\"https://api.yijianlianjsqa.com/\"")
buildConfigField("boolean", "is_free", "false")
buildConfigField("String", "productId", "\"2222\"")
buildConfigField("String", "productId", "\"22222\"")
manifestPlaceholders(
mapOf(
"APP_NAME" to "@string/wf",
@ -178,7 +178,7 @@ subprojects {
}
}
sourceSets {
getByName("2222") {
getByName("22222") {
java.srcDirs("src/foss/java")
}
}