mirror of
https://github.com/SagerNet/sing-box-for-android.git
synced 2025-04-01 19:07:37 +03:00
Replace appcenter with google services
This commit is contained in:
parent
0f9441c650
commit
446ffa4a4d
14 changed files with 182 additions and 187 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,4 +8,5 @@
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
/app/libs/
|
/app/libs/
|
||||||
|
/service-account-credentials.json
|
|
@ -5,6 +5,9 @@ plugins {
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlin-parcelize'
|
id 'kotlin-parcelize'
|
||||||
id 'com.google.devtools.ksp'
|
id 'com.google.devtools.ksp'
|
||||||
|
id 'com.google.gms.google-services'
|
||||||
|
id 'com.google.firebase.crashlytics'
|
||||||
|
id 'com.github.triplet.play'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -39,13 +42,15 @@ android {
|
||||||
if (getProps("KEYSTORE_PASS") != "") {
|
if (getProps("KEYSTORE_PASS") != "") {
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
buildConfigField("String", "APPCENTER_SECRET", "\"" + getProps("APPCENTER_SECRET") + "\"")
|
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
buildConfigField("String", "APPCENTER_SECRET", "\"" + getProps("APPCENTER_SECRET") + "\"")
|
firebaseCrashlytics {
|
||||||
|
nativeSymbolUploadEnabled true
|
||||||
|
unstrippedNativeLibsDir true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,14 +101,14 @@ dependencies {
|
||||||
implementation 'com.blacksquircle.ui:editorkit:2.2.0'
|
implementation 'com.blacksquircle.ui:editorkit:2.2.0'
|
||||||
implementation 'com.blacksquircle.ui:language-json:2.2.0'
|
implementation 'com.blacksquircle.ui:language-json:2.2.0'
|
||||||
|
|
||||||
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.2'
|
|
||||||
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.2'
|
|
||||||
implementation 'com.microsoft.appcenter:appcenter-distribute:5.0.2'
|
|
||||||
|
|
||||||
implementation('org.smali:dexlib2:2.5.2') {
|
implementation('org.smali:dexlib2:2.5.2') {
|
||||||
exclude group: 'com.google.guava', module: 'guava'
|
exclude group: 'com.google.guava', module: 'guava'
|
||||||
}
|
}
|
||||||
implementation('com.google.guava:guava:32.1.2-android')
|
implementation('com.google.guava:guava:32.1.2-android')
|
||||||
|
|
||||||
|
implementation platform('com.google.firebase:firebase-bom:32.4.0')
|
||||||
|
implementation 'com.google.firebase:firebase-crashlytics-ktx'
|
||||||
|
implementation 'com.google.android.play:app-update-ktx:2.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getProps("APPCENTER_TOKEN") != "") {
|
if (getProps("APPCENTER_TOKEN") != "") {
|
||||||
|
@ -122,6 +127,15 @@ if (getProps("APPCENTER_TOKEN") != "") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def playCredentialsJSON = rootProject.file("service-account-credentials.json")
|
||||||
|
if (playCredentialsJSON.exists()) {
|
||||||
|
play {
|
||||||
|
serviceAccountCredentials = playCredentialsJSON
|
||||||
|
defaultToAppBundles = true
|
||||||
|
track = 'beta'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType(KotlinCompile.class).configureEach {
|
tasks.withType(KotlinCompile.class).configureEach {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
|
|
29
app/google-services.json
Normal file
29
app/google-services.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "548801210715",
|
||||||
|
"project_id": "sing-b0x",
|
||||||
|
"storage_bucket": "sing-b0x.appspot.com"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:548801210715:android:2c3bce07700eecb54d527e",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "io.nekohasekai.sfa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyDzb5nuF2qyw-AW0opn4Ymi2QGuJ6dZyYo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
|
@ -39,6 +39,10 @@
|
||||||
android:name="android.app.shortcuts"
|
android:name="android.app.shortcuts"
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_crashlytics_collection_enabled"
|
||||||
|
android:value="false" />
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
@ -173,11 +177,6 @@
|
||||||
android:resource="@xml/cache_paths" />
|
android:resource="@xml/cache_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="com.microsoft.appcenter.distribute.UpdateReceiver"
|
|
||||||
tools:node="remove" />
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -4,7 +4,7 @@ object SettingsKey {
|
||||||
|
|
||||||
const val SELECTED_PROFILE = "selected_profile"
|
const val SELECTED_PROFILE = "selected_profile"
|
||||||
const val SERVICE_MODE = "service_mode"
|
const val SERVICE_MODE = "service_mode"
|
||||||
const val ANALYTICS_ALLOWED = "analytics_allowed"
|
const val ERROR_REPORTING_ENABLED = "error_reporting_enabled"
|
||||||
const val CHECK_UPDATE_ENABLED = "check_update_enabled"
|
const val CHECK_UPDATE_ENABLED = "check_update_enabled"
|
||||||
const val DISABLE_MEMORY_LIMIT = "disable_memory_limit"
|
const val DISABLE_MEMORY_LIMIT = "disable_memory_limit"
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,11 @@ object Settings {
|
||||||
var serviceMode by dataStore.string(SettingsKey.SERVICE_MODE) { ServiceMode.NORMAL }
|
var serviceMode by dataStore.string(SettingsKey.SERVICE_MODE) { ServiceMode.NORMAL }
|
||||||
var startedByUser by dataStore.boolean(SettingsKey.STARTED_BY_USER)
|
var startedByUser by dataStore.boolean(SettingsKey.STARTED_BY_USER)
|
||||||
|
|
||||||
const val ANALYSIS_UNKNOWN = -1
|
const val ERROR_REPORTING_UNKNOWN = -1
|
||||||
const val ANALYSIS_ALLOWED = 0
|
const val ERROR_REPORTING_ALLOWED = 0
|
||||||
const val ANALYSIS_DISALLOWED = 1
|
const val ERROR_REPORTING_DISALLOWED = 1
|
||||||
|
|
||||||
var analyticsAllowed by dataStore.int(SettingsKey.ANALYTICS_ALLOWED) { ANALYSIS_UNKNOWN }
|
var errorReportingEnabled by dataStore.int(SettingsKey.ERROR_REPORTING_ENABLED) { ERROR_REPORTING_UNKNOWN }
|
||||||
var checkUpdateEnabled by dataStore.boolean(SettingsKey.CHECK_UPDATE_ENABLED) { true }
|
var checkUpdateEnabled by dataStore.boolean(SettingsKey.CHECK_UPDATE_ENABLED) { true }
|
||||||
var disableMemoryLimit by dataStore.boolean(SettingsKey.DISABLE_MEMORY_LIMIT)
|
var disableMemoryLimit by dataStore.boolean(SettingsKey.DISABLE_MEMORY_LIMIT)
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,11 @@ package io.nekohasekai.sfa.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.util.Log
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -18,18 +17,16 @@ import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.microsoft.appcenter.AppCenter
|
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
||||||
import com.microsoft.appcenter.analytics.Analytics
|
import com.google.android.play.core.appupdate.AppUpdateOptions
|
||||||
import com.microsoft.appcenter.crashes.Crashes
|
import com.google.android.play.core.install.model.AppUpdateType
|
||||||
import com.microsoft.appcenter.distribute.Distribute
|
import com.google.android.play.core.install.model.InstallStatus
|
||||||
import com.microsoft.appcenter.distribute.DistributeListener
|
import com.google.android.play.core.install.model.UpdateAvailability
|
||||||
import com.microsoft.appcenter.distribute.ReleaseDetails
|
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||||
import com.microsoft.appcenter.distribute.UpdateAction
|
import com.google.firebase.ktx.Firebase
|
||||||
import com.microsoft.appcenter.utils.AppNameHelper
|
|
||||||
import io.nekohasekai.libbox.Libbox
|
import io.nekohasekai.libbox.Libbox
|
||||||
import io.nekohasekai.libbox.ProfileContent
|
import io.nekohasekai.libbox.ProfileContent
|
||||||
import io.nekohasekai.sfa.Application
|
import io.nekohasekai.sfa.Application
|
||||||
import io.nekohasekai.sfa.BuildConfig
|
|
||||||
import io.nekohasekai.sfa.R
|
import io.nekohasekai.sfa.R
|
||||||
import io.nekohasekai.sfa.bg.ServiceConnection
|
import io.nekohasekai.sfa.bg.ServiceConnection
|
||||||
import io.nekohasekai.sfa.bg.ServiceNotification
|
import io.nekohasekai.sfa.bg.ServiceNotification
|
||||||
|
@ -45,17 +42,16 @@ import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
||||||
import io.nekohasekai.sfa.ui.profile.NewProfileActivity
|
import io.nekohasekai.sfa.ui.profile.NewProfileActivity
|
||||||
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeListener {
|
class MainActivity : AbstractActivity(), ServiceConnection.Callback {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MyActivity"
|
private const val TAG = "MainActivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
@ -86,7 +82,7 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
|
||||||
binding.navView.setupWithNavController(navController)
|
binding.navView.setupWithNavController(navController)
|
||||||
|
|
||||||
reconnect()
|
reconnect()
|
||||||
startAnalysis()
|
startIntegration()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
@ -182,109 +178,76 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
|
||||||
connection.reconnect()
|
connection.reconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startAnalysis() {
|
private fun startIntegration() {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
when (Settings.analyticsAllowed) {
|
if (Settings.errorReportingEnabled == Settings.ERROR_REPORTING_UNKNOWN) {
|
||||||
Settings.ANALYSIS_UNKNOWN -> {
|
withContext(Dispatchers.Main) {
|
||||||
withContext(Dispatchers.Main) {
|
confirmErrorReportingIntegration()
|
||||||
showAnalysisDialog()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings.ANALYSIS_ALLOWED -> {
|
|
||||||
startAnalysisInternal()
|
|
||||||
}
|
}
|
||||||
|
} else if (Settings.checkUpdateEnabled) {
|
||||||
|
checkUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showAnalysisDialog() {
|
private fun checkUpdate() {
|
||||||
val builder = MaterialAlertDialogBuilder(this)
|
val appUpdateManager = AppUpdateManagerFactory.create(this)
|
||||||
.setTitle(getString(R.string.analytics_title))
|
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
|
||||||
.setMessage(getString(R.string.analytics_message))
|
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
when (appUpdateInfo.updateAvailability()) {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
UpdateAvailability.UPDATE_NOT_AVAILABLE -> {
|
||||||
Settings.analyticsAllowed = Settings.ANALYSIS_ALLOWED
|
Log.d(TAG, "checkUpdate: not available")
|
||||||
startAnalysisInternal()
|
}
|
||||||
|
|
||||||
|
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> {
|
||||||
|
when (appUpdateInfo.installStatus()) {
|
||||||
|
InstallStatus.DOWNLOADED -> {
|
||||||
|
appUpdateManager.completeUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAvailability.UPDATE_AVAILABLE -> {
|
||||||
|
if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
|
||||||
|
appUpdateManager.startUpdateFlow(
|
||||||
|
appUpdateInfo,
|
||||||
|
this,
|
||||||
|
AppUpdateOptions.newBuilder(AppUpdateType.FLEXIBLE).build()
|
||||||
|
)
|
||||||
|
} else if (appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
|
||||||
|
appUpdateManager.startUpdateFlow(
|
||||||
|
appUpdateInfo,
|
||||||
|
this,
|
||||||
|
AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAvailability.UNKNOWN -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(getString(R.string.no_thanks)) { _, _ ->
|
}
|
||||||
|
appUpdateInfoTask.addOnFailureListener {
|
||||||
|
Log.e(TAG, "checkUpdate: ", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun confirmErrorReportingIntegration() {
|
||||||
|
val builder = MaterialAlertDialogBuilder(this).setTitle(getString(R.string.error_reporting))
|
||||||
|
.setMessage(R.string.error_reporting_message)
|
||||||
|
.setPositiveButton(getString(R.string.ok)) { _, _ ->
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
Settings.analyticsAllowed = Settings.ANALYSIS_DISALLOWED
|
Settings.errorReportingEnabled = Settings.ERROR_REPORTING_ALLOWED
|
||||||
|
Firebase.crashlytics.setCrashlyticsCollectionEnabled(true)
|
||||||
|
}
|
||||||
|
}.setNegativeButton(getString(R.string.no_thanks)) { _, _ ->
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
Settings.errorReportingEnabled = Settings.ERROR_REPORTING_DISALLOWED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCatching { builder.show() }
|
runCatching { builder.show() }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun startAnalysisInternal() {
|
|
||||||
if (BuildConfig.APPCENTER_SECRET.isBlank()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Distribute.setListener(this)
|
|
||||||
runCatching {
|
|
||||||
AppCenter.start(
|
|
||||||
application,
|
|
||||||
BuildConfig.APPCENTER_SECRET,
|
|
||||||
Analytics::class.java,
|
|
||||||
Crashes::class.java,
|
|
||||||
Distribute::class.java,
|
|
||||||
)
|
|
||||||
if (!Settings.checkUpdateEnabled) {
|
|
||||||
Distribute.disableAutomaticCheckForUpdate()
|
|
||||||
}
|
|
||||||
}.onFailure {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
errorDialogBuilder(it).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReleaseAvailable(activity: Activity, releaseDetails: ReleaseDetails): Boolean {
|
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
delay(2000L)
|
|
||||||
runCatching {
|
|
||||||
onReleaseAvailable0(releaseDetails)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onReleaseAvailable0(releaseDetails: ReleaseDetails) {
|
|
||||||
val builder = MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(getString(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_title))
|
|
||||||
var message = if (releaseDetails.isMandatoryUpdate) {
|
|
||||||
getString(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_message_mandatory)
|
|
||||||
} else {
|
|
||||||
getString(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_message_optional)
|
|
||||||
}
|
|
||||||
message = String.format(
|
|
||||||
message,
|
|
||||||
AppNameHelper.getAppName(this),
|
|
||||||
releaseDetails.shortVersion,
|
|
||||||
releaseDetails.version
|
|
||||||
)
|
|
||||||
builder.setMessage(message)
|
|
||||||
builder.setPositiveButton(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_download) { _, _ ->
|
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, releaseDetails.downloadUrl))
|
|
||||||
}
|
|
||||||
builder.setCancelable(false)
|
|
||||||
if (!releaseDetails.isMandatoryUpdate) {
|
|
||||||
builder.setNegativeButton(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_postpone) { _, _ ->
|
|
||||||
Distribute.notifyUpdateAction(UpdateAction.POSTPONE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(releaseDetails.releaseNotes) && releaseDetails.releaseNotesUrl != null) {
|
|
||||||
builder.setNeutralButton(com.microsoft.appcenter.distribute.R.string.appcenter_distribute_update_dialog_view_release_notes) { _, _ ->
|
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, releaseDetails.releaseNotesUrl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNoReleaseAvailable(activity: Activity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun startService() {
|
fun startService() {
|
||||||
if (!ServiceNotification.checkPermission()) {
|
if (!ServiceNotification.checkPermission()) {
|
||||||
|
|
|
@ -11,8 +11,8 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.microsoft.appcenter.AppCenter
|
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||||
import com.microsoft.appcenter.distribute.Distribute
|
import com.google.firebase.ktx.Firebase
|
||||||
import io.nekohasekai.libbox.Libbox
|
import io.nekohasekai.libbox.Libbox
|
||||||
import io.nekohasekai.sfa.Application
|
import io.nekohasekai.sfa.Application
|
||||||
import io.nekohasekai.sfa.R
|
import io.nekohasekai.sfa.R
|
||||||
|
@ -60,31 +60,18 @@ class SettingsFragment : Fragment() {
|
||||||
reloadSettings()
|
reloadSettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.appCenterEnabled.addTextChangedListener {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
val allowed = EnabledType.valueOf(it).boolValue
|
|
||||||
Settings.analyticsAllowed =
|
|
||||||
if (allowed) Settings.ANALYSIS_ALLOWED else Settings.ANALYSIS_DISALLOWED
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
binding.checkUpdateEnabled.isEnabled = allowed
|
|
||||||
}
|
|
||||||
if (!allowed) {
|
|
||||||
AppCenter.setEnabled(false)
|
|
||||||
} else {
|
|
||||||
if (!AppCenter.isConfigured()) {
|
|
||||||
activity.startAnalysisInternal()
|
|
||||||
}
|
|
||||||
AppCenter.setEnabled(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.checkUpdateEnabled.addTextChangedListener {
|
binding.checkUpdateEnabled.addTextChangedListener {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val newValue = EnabledType.valueOf(it).boolValue
|
val newValue = EnabledType.valueOf(it).boolValue
|
||||||
Settings.checkUpdateEnabled = newValue
|
Settings.checkUpdateEnabled = newValue
|
||||||
if (!newValue) {
|
}
|
||||||
Distribute.disableAutomaticCheckForUpdate()
|
}
|
||||||
}
|
binding.errorReportingEnabled.addTextChangedListener {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val newValue = EnabledType.valueOf(it).boolValue
|
||||||
|
Settings.errorReportingEnabled =
|
||||||
|
if (newValue) Settings.ERROR_REPORTING_ALLOWED else Settings.ERROR_REPORTING_DISALLOWED
|
||||||
|
Firebase.crashlytics.setCrashlyticsCollectionEnabled(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.disableMemoryLimit.addTextChangedListener {
|
binding.disableMemoryLimit.addTextChangedListener {
|
||||||
|
@ -128,23 +115,22 @@ class SettingsFragment : Fragment() {
|
||||||
(activity.getExternalFilesDir(null) ?: activity.filesDir)
|
(activity.getExternalFilesDir(null) ?: activity.filesDir)
|
||||||
.walkTopDown().filter { it.isFile }.map { it.length() }.sum()
|
.walkTopDown().filter { it.isFile }.map { it.length() }.sum()
|
||||||
)
|
)
|
||||||
val appCenterEnabled = Settings.analyticsAllowed == Settings.ANALYSIS_ALLOWED
|
val errorReportingEnabled = Settings.errorReportingEnabled == Settings.ERROR_REPORTING_ALLOWED
|
||||||
val checkUpdateEnabled = Settings.checkUpdateEnabled
|
val checkUpdateEnabled = Settings.checkUpdateEnabled
|
||||||
val removeBackgroudPermissionPage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
val removeBackgroundPermissionPage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Application.powerManager.isIgnoringBatteryOptimizations(Application.application.packageName)
|
Application.powerManager.isIgnoringBatteryOptimizations(Application.application.packageName)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.dataSizeText.text = dataSize
|
binding.dataSizeText.text = dataSize
|
||||||
binding.appCenterEnabled.text = EnabledType.from(appCenterEnabled).name
|
binding.errorReportingEnabled.text = EnabledType.from(errorReportingEnabled).name
|
||||||
binding.appCenterEnabled.setSimpleItems(R.array.enabled)
|
binding.errorReportingEnabled.setSimpleItems(R.array.enabled)
|
||||||
binding.checkUpdateEnabled.isEnabled = appCenterEnabled
|
|
||||||
binding.checkUpdateEnabled.text = EnabledType.from(checkUpdateEnabled).name
|
binding.checkUpdateEnabled.text = EnabledType.from(checkUpdateEnabled).name
|
||||||
binding.checkUpdateEnabled.setSimpleItems(R.array.enabled)
|
binding.checkUpdateEnabled.setSimpleItems(R.array.enabled)
|
||||||
binding.disableMemoryLimit.text = EnabledType.from(!Settings.disableMemoryLimit).name
|
binding.disableMemoryLimit.text = EnabledType.from(!Settings.disableMemoryLimit).name
|
||||||
binding.disableMemoryLimit.setSimpleItems(R.array.enabled)
|
binding.disableMemoryLimit.setSimpleItems(R.array.enabled)
|
||||||
binding.backgroundPermissionCard.isGone = removeBackgroudPermissionPage
|
binding.backgroundPermissionCard.isGone = removeBackgroundPermissionPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ class PerAppProxyActivity : AbstractActivity() {
|
||||||
|
|
||||||
if (scanResult.isEmpty()) {
|
if (scanResult.isEmpty()) {
|
||||||
MaterialAlertDialogBuilder(this@PerAppProxyActivity)
|
MaterialAlertDialogBuilder(this@PerAppProxyActivity)
|
||||||
.setTitle(R.string.message)
|
.setTitle(R.string.title_scan_result)
|
||||||
.setMessage(R.string.message_scan_app_no_apps_found)
|
.setMessage(R.string.message_scan_app_no_apps_found)
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
.show()
|
.show()
|
||||||
|
|
1
app/src/main/play/release-notes/en-US/beta.txt
Normal file
1
app/src/main/play/release-notes/en-US/beta.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fixes and improvements
|
|
@ -30,17 +30,17 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/title_app_center"
|
android:text="@string/title_app_settings"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge" />
|
android:textAppearance="?attr/textAppearanceTitleLarge" />
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/appCenterEnabled"
|
android:id="@+id/checkUpdateEnabled"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="8dp"
|
||||||
android:hint="@string/enabled">
|
android:hint="@string/check_update">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -52,12 +52,12 @@
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/checkUpdateEnabled"
|
android:id="@+id/errorReportingEnabled"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:hint="@string/check_update">
|
android:hint="@string/error_reporting">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -95,27 +95,11 @@
|
||||||
|
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/disableMemoryLimit"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/memory_limit">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="none"
|
|
||||||
android:text="@string/enabled"
|
|
||||||
app:simpleItems="@array/enabled" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp">
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="?attr/textAppearanceTitleSmall"
|
style="?attr/textAppearanceTitleSmall"
|
||||||
|
@ -154,10 +138,27 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/disableMemoryLimit"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/memory_limit">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:text="@string/enabled"
|
||||||
|
app:simpleItems="@array/enabled" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical|end"
|
android:gravity="center_vertical|end"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
<string name="app_name">sing-box</string>
|
<string name="app_name">sing-box</string>
|
||||||
|
|
||||||
<string name="stop">Stop</string>
|
<string name="stop">Stop</string>
|
||||||
|
<string name="ok">Ok</string>
|
||||||
|
<string name="no_thanks">No, thanks</string>
|
||||||
|
|
||||||
<string name="title_dashboard">Dashboard</string>
|
<string name="title_dashboard">Dashboard</string>
|
||||||
<string name="title_configuration">Profiles</string>
|
<string name="title_configuration">Profiles</string>
|
||||||
|
@ -80,15 +82,10 @@
|
||||||
<string name="core_version">Version</string>
|
<string name="core_version">Version</string>
|
||||||
<string name="core">Core</string>
|
<string name="core">Core</string>
|
||||||
<string name="core_data_size">Data Size</string>
|
<string name="core_data_size">Data Size</string>
|
||||||
<string name="analytics_title">Analytics</string>
|
|
||||||
<string name="analytics_message">Would you like to give SFA permission to collect analytics, send crash reports, and check update through AppCenter?</string>
|
|
||||||
<string name="no_thanks">No, thanks</string>
|
|
||||||
<string name="check_update">Check Update</string>
|
<string name="check_update">Check Update</string>
|
||||||
<string name="title_app_center">App Center</string>
|
<string name="error_reporting">Error Reporting</string>
|
||||||
<string name="title_feedback">Feedback</string>
|
<string name="error_reporting_message">Would you like to allow sing to send error reports to developers via Firebase Crashlytics?</string>
|
||||||
<string name="message">Message</string>
|
<string name="title_app_settings">App Settings</string>
|
||||||
<string name="send">Send</string>
|
|
||||||
<string name="send_feedback">Send Feedback</string>
|
|
||||||
<string name="about_title">About</string>
|
<string name="about_title">About</string>
|
||||||
<string name="app_description">Android client for sing-box, the universal proxy platform.</string>
|
<string name="app_description">Android client for sing-box, the universal proxy platform.</string>
|
||||||
<string name="documentation_button">Documentation</string>
|
<string name="documentation_button">Documentation</string>
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "gradle.plugin.com.betomorrow.gradle:appcenter-plugin:2.0.4"
|
classpath "gradle.plugin.com.betomorrow.gradle:appcenter-plugin:2.0.4"
|
||||||
|
// https://github.com/invertase/react-native-firebase/issues/6983
|
||||||
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,5 +12,7 @@ plugins {
|
||||||
id 'com.android.library' version '8.1.2' apply false
|
id 'com.android.library' version '8.1.2' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
|
||||||
id 'com.google.devtools.ksp' version '1.9.0-1.0.12' apply false
|
id 'com.google.devtools.ksp' version '1.9.0-1.0.12' apply false
|
||||||
|
id 'com.google.gms.google-services' version '4.4.0' apply false
|
||||||
|
id 'com.github.triplet.play' version '3.8.4' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,7 +1,7 @@
|
||||||
#Sun Aug 20 19:43:30 CST 2023
|
#Mon Oct 30 15:48:15 CST 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue