Compare commits

...

5 Commits

Author SHA1 Message Date
Jason A. Donenfeld 5fa2e6164f version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-05-13 01:12:50 +02:00
Jason A. Donenfeld 9972d9607e gradle: sort lines in localeConfig
This works around https://issuetracker.google.com/issues/281825213

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-05-13 01:12:15 +02:00
Jason A. Donenfeld 6a72194106 ui: use modal corruption alert instead of snackbar
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-05-11 13:50:26 +02:00
Jason A. Donenfeld 254e6164d7 ui: use applicationScope helper when possible
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-05-11 02:57:38 +02:00
Jason A. Donenfeld 675386241b ui: cleanup quick tile and modernize
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-05-11 02:52:50 +02:00
7 changed files with 89 additions and 66 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
wireguardVersionCode=504
wireguardVersionName=1.0.20230510
wireguardVersionCode=505
wireguardVersionName=1.0.20230512
wireguardPackageName=com.wireguard.android
# When configured, Gradle will run in incubating parallel mode.
+12
View File
@@ -2,6 +2,7 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import com.android.build.gradle.tasks.GenerateLocaleConfigTask
// Grotesque workaround for https://issuetracker.google.com/issues/279780940
System.setProperty("com.android.tools.r8.disableApiModeling", "1")
@@ -94,3 +95,14 @@ tasks.withType<JavaCompile>().configureEach {
tasks.withType<KotlinCompile>().configureEach {
compilerOptions.jvmTarget.set(JvmTarget.JVM_17)
}
// Grotesque workaround for https://issuetracker.google.com/issues/281825213
tasks.withType<GenerateLocaleConfigTask>().configureEach {
doLast {
localeConfig.asFileTree.files.forEach {
val lines = it.readLines()
val newLines = lines.subList(0, 2) + lines.subList(2, lines.size - 1).sorted() + lines.last()
it.writeText(newLines.joinToString(separator = "\n"))
}
}
}
+4
View File
@@ -143,6 +143,10 @@
<meta-data
android:name="android.service.quicksettings.ACTIVE_TILE"
android:value="false" />
<meta-data
android:name="android.service.quicksettings.TOGGLEABLE_TILE"
android:value="true" />
</service>
<meta-data
@@ -49,28 +49,26 @@ class QuickTileService : TileService() {
}
override fun onClick() {
if (tunnel != null) {
unlockAndRun {
val tile = qsTile
if (tile != null) {
tile.icon = if (tile.icon == iconOn) iconOff else iconOn
tile.updateTile()
}
applicationScope.launch {
try {
tunnel!!.setStateAsync(Tunnel.State.TOGGLE)
updateTile()
} catch (_: Throwable) {
val toggleIntent = Intent(this@QuickTileService, TunnelToggleActivity::class.java)
toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(toggleIntent)
when (val tunnel = tunnel) {
null -> {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(intent)
}
else -> {
unlockAndRun {
applicationScope.launch {
try {
tunnel.setStateAsync(Tunnel.State.TOGGLE)
updateTile()
} catch (_: Throwable) {
val toggleIntent = Intent(this@QuickTileService, TunnelToggleActivity::class.java)
toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(toggleIntent)
}
}
}
}
} else {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(intent)
}
}
@@ -84,15 +82,13 @@ class QuickTileService : TileService() {
val icon = SlashDrawable(resources.getDrawable(R.drawable.ic_tile, Application.get().theme))
icon.setAnimationEnabled(false) /* Unfortunately we can't have animations, since Icons are marshaled. */
icon.setSlashed(false)
var b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
?: return
var b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888) ?: return
var c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
iconOn = Icon.createWithBitmap(b)
icon.setSlashed(true)
b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
?: return
b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888) ?: return
c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
@@ -106,12 +102,12 @@ class QuickTileService : TileService() {
override fun onStartListening() {
Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback)
if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
tunnel?.addOnPropertyChangedCallback(onStateChangedCallback)
updateTile()
}
override fun onStopListening() {
if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
tunnel?.removeOnPropertyChangedCallback(onStateChangedCallback)
Application.getTunnelManager().removeOnPropertyChangedCallback(onTunnelChangedCallback)
}
@@ -127,26 +123,24 @@ class QuickTileService : TileService() {
// Update the tunnel.
val newTunnel = Application.getTunnelManager().lastUsedTunnel
if (newTunnel != tunnel) {
if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
tunnel?.removeOnPropertyChangedCallback(onStateChangedCallback)
tunnel = newTunnel
if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
tunnel?.addOnPropertyChangedCallback(onStateChangedCallback)
}
// Update the tile contents.
val label: String
val state: Int
val tile = qsTile
if (tunnel != null) {
label = tunnel!!.name
state = if (tunnel!!.state == Tunnel.State.UP) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
} else {
label = getString(R.string.app_name)
state = Tile.STATE_INACTIVE
}
if (tile == null) return
tile.label = label
if (tile.state != state) {
tile.icon = if (state == Tile.STATE_ACTIVE) iconOn else iconOff
tile.state = state
val tile = qsTile ?: return
when (val tunnel = tunnel) {
null -> {
tile.label = getString(R.string.app_name)
tile.state = Tile.STATE_INACTIVE
tile.icon = iconOff
}
else -> {
tile.label = tunnel.name
tile.state = if (tunnel.state == Tunnel.State.UP) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
tile.icon = if (tunnel.state == Tunnel.State.UP) iconOn else iconOff
}
}
tile.updateTile()
}
@@ -157,14 +151,16 @@ class QuickTileService : TileService() {
sender.removeOnPropertyChangedCallback(this)
return
}
if (propertyId != 0 && propertyId != BR.state) return
if (propertyId != 0 && propertyId != BR.state)
return
updateTile()
}
}
private inner class OnTunnelChangedCallback : OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
if (propertyId != 0 && propertyId != BR.lastUsedTunnel) return
if (propertyId != 0 && propertyId != BR.lastUsedTunnel)
return
updateTile()
}
}
@@ -12,6 +12,7 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.R
@@ -34,7 +35,6 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
private val statusSnackbar = makeSnackbar(fragment, view, anchor)
private var showingAction: Boolean = false
private var showingStatus: Boolean = false
private var permanentAction: Boolean = false
private fun makeSnackbar(fragment: Fragment, view: View, anchor: View?): Snackbar {
val snackbar = Snackbar.make(fragment.requireContext(), view, "", Snackbar.LENGTH_INDEFINITE)
@@ -49,7 +49,7 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
snackbar.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(snackbar: Snackbar?, @DismissEvent event: Int) {
super.onDismissed(snackbar, event)
if (event == DISMISS_EVENT_MANUAL || (event == DISMISS_EVENT_ACTION && !permanentAction) ||
if (event == DISMISS_EVENT_MANUAL || event == DISMISS_EVENT_ACTION ||
(snackbar == actionSnackbar && !showingAction) || (snackbar == statusSnackbar && !showingStatus)
)
return
@@ -62,12 +62,11 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
return snackbar
}
fun showAction(text: String, action: String, permanent: Boolean = false, listener: View.OnClickListener) {
fun showAction(text: String, action: String, listener: View.OnClickListener) {
if (showingStatus) {
showingStatus = false
statusSnackbar.dismiss()
}
permanentAction = permanent
actionSnackbar.setText(text)
actionSnackbar.setAction(action, listener)
if (!showingAction) {
@@ -148,15 +147,25 @@ class SnackbarUpdateShower(private val fragment: Fragment) {
}
is Updater.Progress.Corrupt -> {
snackbar.showAction(context.getString(R.string.updater_corrupt), context.getString(R.string.updater_corrupt_navigate), true) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(progress.downloadUrl)
try {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.updater_corrupt_title)
.setMessage(R.string.updater_corrupt_message)
.setPositiveButton(R.string.updater_corrupt_navigate) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(progress.downloadUrl)
try {
context.startActivity(intent)
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}.setCancelable(false).setOnDismissListener {
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_HOME)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}
System.exit(0)
}.show()
}
}
}.launchIn(fragment.lifecycleScope)
@@ -21,6 +21,7 @@ import com.wireguard.android.Application
import com.wireguard.android.BuildConfig
import com.wireguard.android.activity.MainActivity
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -76,7 +77,7 @@ object Updater {
object Complete : Progress()
class Available(val version: String) : Progress() {
fun update() {
Application.getCoroutineScope().launch {
applicationScope.launch {
UserKnobs.setUpdaterNewerVersionConsented(version)
}
}
@@ -100,7 +101,7 @@ object Updater {
}
fun markAsDone() {
Application.getCoroutineScope().launch {
applicationScope.launch {
if (installerActive())
return@launch
delay(7.seconds)
@@ -333,13 +334,13 @@ object Updater {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val id = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, 0)
val userIntervention = IntentCompat.getParcelableExtra(intent, Intent.EXTRA_INTENT, Intent::class.java)!!
Application.getCoroutineScope().launch {
applicationScope.launch {
emitProgress(Progress.NeedsUserIntervention(userIntervention, id))
}
}
PackageInstaller.STATUS_SUCCESS -> {
Application.getCoroutineScope().launch {
applicationScope.launch {
emitProgress(Progress.Complete)
}
context.applicationContext.unregisterReceiver(this)
@@ -352,7 +353,7 @@ object Updater {
} catch (_: SecurityException) {
}
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) ?: "Installation error $status"
Application.getCoroutineScope().launch {
applicationScope.launch {
val e = Exception(message)
Log.e(TAG, "Update failure", e)
emitProgress(Progress.Failure(e))
@@ -414,14 +415,14 @@ object Updater {
UserKnobs.updaterNewerVersionConsented.firstOrNull()?.let { Version(it) > CURRENT_VERSION } != true
)
emitProgress(Progress.Available(ver))
}.launchIn(Application.getCoroutineScope())
}.launchIn(applicationScope)
UserKnobs.updaterNewerVersionConsented.onEach { ver ->
if (ver != null && Version(ver) > CURRENT_VERSION)
updaterScope.launch {
downloadAndUpdateWrapErrors()
}
}.launchIn(Application.getCoroutineScope())
}.launchIn(applicationScope)
}
class AppUpdatedReceiver : BroadcastReceiver() {
+2 -1
View File
@@ -240,7 +240,8 @@
<string name="updater_download_progress_nototal">Downloading update: %s</string>
<string name="updater_installing">Installing update…</string>
<string name="updater_failure">Update failure: %s. Will retry momentarily…</string>
<string name="updater_corrupt">This application is corrupt. Please re-download it.</string>
<string name="updater_corrupt_title">Application Corrupt</string>
<string name="updater_corrupt_message">This application is corrupt. Please re-download the APK from the website linked below. After, uninstall this application, and reinstall it from downloaded APK.</string>
<string name="updater_corrupt_navigate">Open Website</string>
<string name="version_summary">%1$s backend %2$s</string>
<string name="version_summary_checking">Checking %s backend version</string>