Replace appcenter with google services

This commit is contained in:
世界 2023-10-31 13:44:42 +08:00
parent 0f9441c650
commit 446ffa4a4d
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
14 changed files with 182 additions and 187 deletions

3
.gitignore vendored
View file

@ -8,4 +8,5 @@
.externalNativeBuild
.cxx
local.properties
/app/libs/
/app/libs/
/service-account-credentials.json

View file

@ -5,6 +5,9 @@ plugins {
id 'kotlin-android'
id 'kotlin-parcelize'
id 'com.google.devtools.ksp'
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
id 'com.github.triplet.play'
}
android {
@ -39,13 +42,15 @@ android {
if (getProps("KEYSTORE_PASS") != "") {
signingConfig signingConfigs.release
}
buildConfigField("String", "APPCENTER_SECRET", "\"" + getProps("APPCENTER_SECRET") + "\"")
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
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: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') {
exclude group: 'com.google.guava', module: 'guava'
}
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") != "") {
@ -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 {
kotlinOptions {
jvmTarget = "1.8"

29
app/google-services.json Normal file
View 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"
}

View file

@ -39,6 +39,10 @@
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -173,11 +177,6 @@
android:resource="@xml/cache_paths" />
</provider>
<receiver
android:name="com.microsoft.appcenter.distribute.UpdateReceiver"
tools:node="remove" />
</application>
</manifest>

View file

@ -4,7 +4,7 @@ object SettingsKey {
const val SELECTED_PROFILE = "selected_profile"
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 DISABLE_MEMORY_LIMIT = "disable_memory_limit"

View file

@ -38,11 +38,11 @@ object Settings {
var serviceMode by dataStore.string(SettingsKey.SERVICE_MODE) { ServiceMode.NORMAL }
var startedByUser by dataStore.boolean(SettingsKey.STARTED_BY_USER)
const val ANALYSIS_UNKNOWN = -1
const val ANALYSIS_ALLOWED = 0
const val ANALYSIS_DISALLOWED = 1
const val ERROR_REPORTING_UNKNOWN = -1
const val ERROR_REPORTING_ALLOWED = 0
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 disableMemoryLimit by dataStore.boolean(SettingsKey.DISABLE_MEMORY_LIMIT)

View file

@ -2,12 +2,11 @@ package io.nekohasekai.sfa.ui
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
@ -18,18 +17,16 @@ import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.microsoft.appcenter.AppCenter
import com.microsoft.appcenter.analytics.Analytics
import com.microsoft.appcenter.crashes.Crashes
import com.microsoft.appcenter.distribute.Distribute
import com.microsoft.appcenter.distribute.DistributeListener
import com.microsoft.appcenter.distribute.ReleaseDetails
import com.microsoft.appcenter.distribute.UpdateAction
import com.microsoft.appcenter.utils.AppNameHelper
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.libbox.ProfileContent
import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.BuildConfig
import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.bg.ServiceConnection
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.shared.AbstractActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Date
import java.util.LinkedList
class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeListener {
class MainActivity : AbstractActivity(), ServiceConnection.Callback {
companion object {
private const val TAG = "MyActivity"
private const val TAG = "MainActivity"
}
private lateinit var binding: ActivityMainBinding
@ -86,7 +82,7 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
binding.navView.setupWithNavController(navController)
reconnect()
startAnalysis()
startIntegration()
}
override fun onNewIntent(intent: Intent) {
@ -182,109 +178,76 @@ class MainActivity : AbstractActivity(), ServiceConnection.Callback, DistributeL
connection.reconnect()
}
private fun startAnalysis() {
private fun startIntegration() {
lifecycleScope.launch(Dispatchers.IO) {
when (Settings.analyticsAllowed) {
Settings.ANALYSIS_UNKNOWN -> {
withContext(Dispatchers.Main) {
showAnalysisDialog()
}
}
Settings.ANALYSIS_ALLOWED -> {
startAnalysisInternal()
if (Settings.errorReportingEnabled == Settings.ERROR_REPORTING_UNKNOWN) {
withContext(Dispatchers.Main) {
confirmErrorReportingIntegration()
}
} else if (Settings.checkUpdateEnabled) {
checkUpdate()
}
}
}
private fun showAnalysisDialog() {
val builder = MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.analytics_title))
.setMessage(getString(R.string.analytics_message))
.setPositiveButton(android.R.string.ok) { _, _ ->
lifecycleScope.launch(Dispatchers.IO) {
Settings.analyticsAllowed = Settings.ANALYSIS_ALLOWED
startAnalysisInternal()
private fun checkUpdate() {
val appUpdateManager = AppUpdateManagerFactory.create(this)
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
when (appUpdateInfo.updateAvailability()) {
UpdateAvailability.UPDATE_NOT_AVAILABLE -> {
Log.d(TAG, "checkUpdate: not available")
}
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) {
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() }
}
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")
fun startService() {
if (!ServiceNotification.checkPermission()) {

View file

@ -11,8 +11,8 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isGone
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.microsoft.appcenter.AppCenter
import com.microsoft.appcenter.distribute.Distribute
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.R
@ -60,31 +60,18 @@ class SettingsFragment : Fragment() {
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 {
lifecycleScope.launch(Dispatchers.IO) {
val newValue = EnabledType.valueOf(it).boolValue
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 {
@ -128,23 +115,22 @@ class SettingsFragment : Fragment() {
(activity.getExternalFilesDir(null) ?: activity.filesDir)
.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 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)
} else {
true
}
withContext(Dispatchers.Main) {
binding.dataSizeText.text = dataSize
binding.appCenterEnabled.text = EnabledType.from(appCenterEnabled).name
binding.appCenterEnabled.setSimpleItems(R.array.enabled)
binding.checkUpdateEnabled.isEnabled = appCenterEnabled
binding.errorReportingEnabled.text = EnabledType.from(errorReportingEnabled).name
binding.errorReportingEnabled.setSimpleItems(R.array.enabled)
binding.checkUpdateEnabled.text = EnabledType.from(checkUpdateEnabled).name
binding.checkUpdateEnabled.setSimpleItems(R.array.enabled)
binding.disableMemoryLimit.text = EnabledType.from(!Settings.disableMemoryLimit).name
binding.disableMemoryLimit.setSimpleItems(R.array.enabled)
binding.backgroundPermissionCard.isGone = removeBackgroudPermissionPage
binding.backgroundPermissionCard.isGone = removeBackgroundPermissionPage
}
}

View file

@ -282,7 +282,7 @@ class PerAppProxyActivity : AbstractActivity() {
if (scanResult.isEmpty()) {
MaterialAlertDialogBuilder(this@PerAppProxyActivity)
.setTitle(R.string.message)
.setTitle(R.string.title_scan_result)
.setMessage(R.string.message_scan_app_no_apps_found)
.setPositiveButton(android.R.string.ok, null)
.show()

View file

@ -0,0 +1 @@
Fixes and improvements

View file

@ -30,17 +30,17 @@
<TextView
android:layout_width="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" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/appCenterEnabled"
android:id="@+id/checkUpdateEnabled"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/enabled">
android:layout_marginTop="8dp"
android:hint="@string/check_update">
<AutoCompleteTextView
android:layout_width="match_parent"
@ -52,12 +52,12 @@
</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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:hint="@string/check_update">
android:hint="@string/error_reporting">
<AutoCompleteTextView
android:layout_width="match_parent"
@ -95,27 +95,11 @@
</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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
android:layout_marginTop="8dp">
<TextView
style="?attr/textAppearanceTitleSmall"
@ -154,10 +138,27 @@
</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_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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|end"
android:orientation="horizontal">

View file

@ -2,6 +2,8 @@
<string name="app_name">sing-box</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_configuration">Profiles</string>
@ -80,15 +82,10 @@
<string name="core_version">Version</string>
<string name="core">Core</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="title_app_center">App Center</string>
<string name="title_feedback">Feedback</string>
<string name="message">Message</string>
<string name="send">Send</string>
<string name="send_feedback">Send Feedback</string>
<string name="error_reporting">Error Reporting</string>
<string name="error_reporting_message">Would you like to allow sing to send error reports to developers via Firebase Crashlytics?</string>
<string name="title_app_settings">App Settings</string>
<string name="about_title">About</string>
<string name="app_description">Android client for sing-box, the universal proxy platform.</string>
<string name="documentation_button">Documentation</string>

View file

@ -2,6 +2,8 @@
buildscript {
dependencies {
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 '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.gms.google-services' version '4.4.0' apply false
id 'com.github.triplet.play' version '3.8.4' apply false
}

View file

@ -1,7 +1,7 @@
#Sun Aug 20 19:43:30 CST 2023
#Mon Oct 30 15:48:15 CST 2023
distributionBase=GRADLE_USER_HOME
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
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists