Skip to content

Commit 1de17c5

Browse files
author
NoahAndrews
committed
Merge remote-tracking branch 'origin/main'
2 parents 7bdf67c + 52019d6 commit 1de17c5

File tree

9 files changed

+158
-20
lines changed

9 files changed

+158
-20
lines changed

src/main/AndroidManifest.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
1111
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
1212

13-
<!-- Added by REV Robotics on 2021-04-28 -->
13+
<!-- Added by REV Robotics on 2021-04-28 -->
1414
<uses-permission android:name="com.revrobotics.permission.UPDATE_CONTROL_HUB" />
1515

16+
<!-- Added by REV Robotics on 2021-06-09 -->
17+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
18+
1619
<application
1720
android:name=".MainApplication"
1821
android:label="@string/application_name"

src/main/kotlin/com/revrobotics/InternetAvailableBroadcastReceiver.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.content.BroadcastReceiver
44
import android.content.Context
55
import android.content.Intent
66
import android.util.Log
7+
import nya.kitsunyan.foxydroid.MainActivity
78
import nya.kitsunyan.foxydroid.MainApplication
89
import nya.kitsunyan.foxydroid.service.Connection
910
import nya.kitsunyan.foxydroid.service.SyncService
@@ -20,16 +21,33 @@ class InternetAvailableBroadcastReceiver : BroadcastReceiver() {
2021

2122
override fun onReceive(context: Context, intent: Intent) {
2223
if ("com.revrobotics.revupdateinterface.INTERNET_AVAILABLE" == intent.action) {
23-
Log.i(tag, "Internet is available");
24+
Log.i(tag, "Internet is available")
25+
26+
if (actionWaitingForInternetConnection != null) {
27+
// Tell the main activity to perform the action that was waiting for an Internet connection
28+
context.startActivity(
29+
Intent(MainApplication.instance, MainActivity::class.java)
30+
.apply {
31+
action = MainActivity.ACTION_PERFORM_ACTION_WAITING_ON_INTERNET
32+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
33+
})
34+
35+
// TODO(Noah): Only execute the desired action immediately if the repository has been updated in the last 12 hours,
36+
// or if a specific release was requested.
37+
// Otherwise, wait to perform it until after the automatic sync.
38+
}
2439

2540
if (LastUpdateOfAllReposTracker.timeSinceLastUpdateOfAllRepos.toHours() > 12) {
41+
// TODO(Noah): In the wait for Internet dialog, show that we are connected to the Internet, but are syncing.
2642
Log.i(tag, "It has been more than 12 hours since all repositories were updated; initiating repository sync")
2743
Connection(SyncService::class.java, onBind = { connection, binder ->
2844
// We want to have the foreground notification, because otherwise the app isn't guaranteed to stay running
2945
// long enough to finish.
3046
binder.sync(SyncService.SyncRequest.AUTO_WITH_FOREGROUND_NOTIFICATION)
3147
connection.unbind(MainApplication.instance)
3248
}).bind(MainApplication.instance)
49+
} else {
50+
// TODO(Noah): Move the desired action execution to this block
3351
}
3452
}
3553
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.revrobotics
2+
3+
import android.app.AlertDialog
4+
import android.app.Dialog
5+
import android.content.Intent
6+
import android.os.Bundle
7+
import android.provider.Settings
8+
import androidx.fragment.app.DialogFragment
9+
10+
class RequestInternetDialogFragment: DialogFragment() {
11+
companion object {
12+
var instance: RequestInternetDialogFragment? = null
13+
}
14+
15+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
16+
instance = this
17+
return activity?.let {
18+
val dialog = AlertDialog.Builder(it)
19+
.setMessage("Please connect to the Internet")
20+
.setNegativeButton("Cancel", null)
21+
.setOnDismissListener {
22+
actionWaitingForInternetConnection = null
23+
instance = null
24+
}
25+
// We use a null listener, and later override the underlying onClickListener, so the dialog doesn't get dismissed
26+
.setPositiveButton("Wi-Fi settings", null)
27+
.create()
28+
29+
dialog.setOnShowListener {
30+
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
31+
startActivity(Intent(Settings.ACTION_WIFI_SETTINGS).apply {
32+
putExtra("com.revrobotics.wifiConnectionReason", "Connect to the Internet for software downloads")
33+
})
34+
}
35+
}
36+
37+
return dialog
38+
} ?: throw IllegalStateException("Activity cannot be null")
39+
}
40+
}

src/main/kotlin/com/revrobotics/Util.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import android.app.PendingIntent
77
import android.content.Context
88
import android.content.Intent
99
import android.graphics.Color
10+
import android.net.ConnectivityManager
11+
import android.net.NetworkCapabilities
1012
import nya.kitsunyan.foxydroid.database.Database
1113
import nya.kitsunyan.foxydroid.service.Connection
1214
import nya.kitsunyan.foxydroid.service.DownloadService
@@ -24,6 +26,8 @@ import nya.kitsunyan.foxydroid.MainActivity
2426
import nya.kitsunyan.foxydroid.MainApplication
2527
import nya.kitsunyan.foxydroid.R
2628
import nya.kitsunyan.foxydroid.entity.ProductItem
29+
import nya.kitsunyan.foxydroid.entity.Release
30+
import nya.kitsunyan.foxydroid.entity.Repository
2731
import nya.kitsunyan.foxydroid.utility.extension.resources.getColorFromAttr
2832
import java.time.Duration
2933
import java.time.Instant
@@ -34,10 +38,21 @@ import kotlin.concurrent.thread
3438

3539
val mainThreadHandler = Handler(Looper.getMainLooper())
3640
val notificationManager = MainApplication.instance.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
41+
val connectivityManager = MainApplication.instance.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
42+
val internetAvailable: Boolean
43+
get() = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
44+
@Volatile var actionWaitingForInternetConnection: ActionWaitingForInternetConnection? = null // This is reset to null when RequestInternetDialogFragment is cancelled
3745

3846
private val downloadQueuingExecutor = Executors.newSingleThreadExecutor()
3947

40-
// queueDownloadAndUpdate function added by REV Robotics on 2021-05-09
48+
sealed class ActionWaitingForInternetConnection
49+
object UpdateAll: ActionWaitingForInternetConnection()
50+
data class InstallApk(val packageName: String,
51+
val productName: String,
52+
val repository: Repository,
53+
val release: Release): ActionWaitingForInternetConnection()
54+
// TODO(Noah): Distinguish between "install latest" and "install specific version"
55+
4156
fun queueDownloadAndUpdate(packageName: String, downloadConnection: Connection<DownloadService.Binder, DownloadService>) {
4257
downloadQueuingExecutor.submit {
4358
val repositoryMap = Database.RepositoryAdapter.getAll(null)
@@ -61,8 +76,8 @@ fun queueDownloadAndUpdate(packageName: String, downloadConnection: Connection<D
6176
val release = if (multipleCompatibleReleases) {
6277
compatibleReleases!!
6378
.filter { it.platforms.contains(Android.primaryPlatform) }
64-
.minBy { it.platforms.size }
65-
?: compatibleReleases.minBy { it.platforms.size }
79+
.minByOrNull { it.platforms.size }
80+
?: compatibleReleases.minByOrNull { it.platforms.size }
6681
?: compatibleReleases.firstOrNull()
6782
} else {
6883
compatibleReleases?.firstOrNull()

src/main/kotlin/nya/kitsunyan/foxydroid/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class MainActivity: ScreenActivity() {
77
companion object {
88
const val ACTION_UPDATES = "${BuildConfig.APPLICATION_ID}.intent.action.UPDATES"
99
const val ACTION_UPDATE_ALL = "${BuildConfig.APPLICATION_ID}.intent.action.UPDATE_ALL" // Added by REV Robotics on 2021-06-07
10+
const val ACTION_PERFORM_ACTION_WAITING_ON_INTERNET = "${BuildConfig.APPLICATION_ID}.intent.action.WAITING_FOR_INTERNET" // Added by REV Robotics on 2021-06-15
1011
const val ACTION_INSTALL = "${BuildConfig.APPLICATION_ID}.intent.action.INSTALL"
1112
const val EXTRA_CACHE_FILE_NAME = "${BuildConfig.APPLICATION_ID}.intent.extra.CACHE_FILE_NAME"
1213
}
@@ -17,6 +18,7 @@ class MainActivity: ScreenActivity() {
1718
ACTION_INSTALL -> handleSpecialIntent(SpecialIntent.Install(intent.packageName,
1819
intent.getStringExtra(EXTRA_CACHE_FILE_NAME)))
1920
ACTION_UPDATE_ALL -> handleSpecialIntent(SpecialIntent.UpdateAll) // Added by REV Robotics on 2021-06-07
21+
ACTION_PERFORM_ACTION_WAITING_ON_INTERNET -> handleSpecialIntent(SpecialIntent.PerformActionWaitingOnInternet) // Added by REV Robotics on 2021-06-07
2022
else -> super.handleIntent(intent)
2123
}
2224
}

src/main/kotlin/nya/kitsunyan/foxydroid/MainApplication.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class MainApplication: Application() {
232232
}
233233

234234
// installOsUpdate() function added by REV Robotics on 2021-05-10
235+
// Normally, we'd want to verify that we have Internet access before queueing a download, but the OS update file
236+
// should already be downloaded to the cache at this point.
235237
private fun installOsUpdate() {
236238
Connection(DownloadService::class.java, onBind = { connection, _ ->
237239
mainThreadHandler.postDelayed(::installOsUpdate, 1000)

src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductFragment.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ import androidx.fragment.app.DialogFragment
1919
import androidx.recyclerview.widget.GridLayoutManager
2020
import androidx.recyclerview.widget.LinearLayoutManager
2121
import androidx.recyclerview.widget.RecyclerView
22+
import com.revrobotics.RequestInternetDialogFragment
2223
import com.revrobotics.RevConstants
2324
import com.revrobotics.RevUpdater
25+
import com.revrobotics.InstallApk
26+
import com.revrobotics.actionWaitingForInternetConnection
27+
import com.revrobotics.internetAvailable
2428
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
2529
import io.reactivex.rxjava3.core.Observable
2630
import io.reactivex.rxjava3.disposables.Disposable
@@ -376,7 +380,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
376380
}
377381
val binder = downloadConnection.binder
378382
if (productRepository != null && release != null && binder != null) {
379-
binder.enqueue(packageName, productRepository.first.name, productRepository.second, release)
383+
handleDownloadClick(binder, productRepository, release)
380384
}
381385
Unit
382386
}
@@ -456,8 +460,9 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
456460
else -> {
457461
val productRepository = products.asSequence().filter { it.first.releases.any { it === release } }.firstOrNull()
458462
if (productRepository != null) {
459-
downloadConnection.binder?.enqueue(packageName, productRepository.first.name,
460-
productRepository.second, release)
463+
downloadConnection.binder?.let {
464+
handleDownloadClick(it, productRepository, release)
465+
}
461466
}
462467
}
463468
}
@@ -502,4 +507,15 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
502507
.create()
503508
}
504509
}
510+
511+
// handleDownloadClick function added by REV Robotics on 2021-06-12
512+
private fun handleDownloadClick(downloadServiceBinder: DownloadService.Binder, productRepository: Pair<Product, Repository>, release: Release) {
513+
if (internetAvailable) {
514+
downloadServiceBinder.enqueue(packageName, productRepository.first.name,
515+
productRepository.second, release)
516+
} else {
517+
actionWaitingForInternetConnection = InstallApk(packageName, productRepository.first.name, productRepository.second, release)
518+
RequestInternetDialogFragment().show(childFragmentManager, null)
519+
}
520+
}
505521
}

src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsFragment.kt

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import com.revrobotics.RevUpdater
2121
import com.revrobotics.LastUpdateOfAllReposTracker
2222
import com.revrobotics.LastUpdateOfAllReposTracker.lastUpdateOfAllRepos
2323
import com.revrobotics.LastUpdateOfAllReposTracker.timeSinceLastUpdateOfAllRepos
24+
import com.revrobotics.RequestInternetDialogFragment
25+
import com.revrobotics.UpdateAll
26+
import com.revrobotics.actionWaitingForInternetConnection
27+
import com.revrobotics.internetAvailable
2428
import com.revrobotics.mainThreadHandler
2529
import com.revrobotics.queueDownloadAndUpdate
2630
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@@ -116,7 +120,7 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
116120
LastUpdateOfAllReposTracker.addTimestampChangedCallback(lastDownloadTimestampChangedCallback)
117121
}
118122

119-
// The remainder of this function was reworked by REV Robotics on 2021-05-09 in order to support an Update ALl button
123+
// The remainder of this function was reworked by REV Robotics on 2021-05-09 in order to support an Update All button
120124

121125
downloadConnection = Connection(DownloadService::class.java)
122126
downloadConnection?.bind(requireContext())
@@ -134,15 +138,20 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
134138
RecyclerFastScroller(recyclerView)
135139

136140
updateAllButton.setOnClickListener {
137-
updateAllButton.isEnabled = false
138-
updateAllButton.text = "Updating all software"
139-
RevUpdater.updatingAllSoftware = true
140-
val messagePart1 = "Downloading and installing all updates."
141-
val messagePart2 = "Download progress is displayed in the notification shade, and installation progress is displayed here."
142-
RevUpdater.showOrUpdateDialog("$messagePart1\n\n$messagePart2", activity!!)
143-
RevUpdater.dialogPrefix = "$messagePart1 $messagePart2\n\n"
144-
updateAll()
145-
// TODO(Noah): Reset the text and enable the button when all updates are installed
141+
if (internetAvailable) {
142+
updateAllButton.isEnabled = false
143+
updateAllButton.text = "Updating all software"
144+
RevUpdater.updatingAllSoftware = true
145+
val messagePart1 = "Downloading and installing all updates."
146+
val messagePart2 = "Download progress is displayed in the notification shade, and installation progress is displayed here."
147+
RevUpdater.showOrUpdateDialog("$messagePart1\n\n$messagePart2", activity!!)
148+
RevUpdater.dialogPrefix = "$messagePart1 $messagePart2\n\n"
149+
updateAll()
150+
// TODO(Noah): Reset the text and enable the button when all updates are installed
151+
} else {
152+
actionWaitingForInternetConnection = UpdateAll
153+
RequestInternetDialogFragment().show(childFragmentManager, null)
154+
}
146155
}
147156

148157
this.recyclerView = recyclerView
@@ -276,14 +285,14 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
276285
}
277286

278287
private fun getUpdatesTabEmptyText(): String {
279-
val asOfPrefix = " as of "
280-
288+
// TODO(Noah): If currently syncing, state that.
281289
if (lastUpdateOfAllRepos < LocalDate.parse("2021-01-01").atStartOfDay().toInstant(ZoneOffset.UTC)) {
282290
// If we haven't checked for updates in an impossibly long time, assume we never have.
283291
return "Please connect to the Internet and check for updates"
284292
}
285293

286294
// We have to tell getRelativeTimeSpanString() whether we want the results in minutes, hours, days, or weeks
295+
val asOfPrefix = " as of "
287296
val howLongAgo = when {
288297
timeSinceLastUpdateOfAllRepos < Duration.ofMinutes(10) -> { // Syncing repositories can take a while, so we have a ten minute grace window
289298
""

src/main/kotlin/nya/kitsunyan/foxydroid/screen/ScreenActivity.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,25 @@ import android.content.Intent
55
import android.net.Uri
66
import android.os.Bundle
77
import android.os.Parcel
8+
import android.util.Log
89
import android.view.View
910
import android.view.ViewGroup
1011
import android.view.inputmethod.InputMethodManager
1112
import android.widget.FrameLayout
1213
import android.widget.Toolbar
1314
import androidx.fragment.app.Fragment
1415
import androidx.fragment.app.FragmentActivity
16+
import com.revrobotics.InstallApk
17+
import com.revrobotics.RequestInternetDialogFragment
1518
import com.revrobotics.RevUpdater
19+
import com.revrobotics.UpdateAll
20+
import com.revrobotics.actionWaitingForInternetConnection
1621
import nya.kitsunyan.foxydroid.R
1722
import nya.kitsunyan.foxydroid.content.Cache
1823
import nya.kitsunyan.foxydroid.content.Preferences
1924
import nya.kitsunyan.foxydroid.database.CursorOwner
2025
import nya.kitsunyan.foxydroid.service.Connection
26+
import nya.kitsunyan.foxydroid.service.DownloadService
2127
import nya.kitsunyan.foxydroid.service.SyncService
2228
import nya.kitsunyan.foxydroid.utility.KParcelable
2329
import nya.kitsunyan.foxydroid.utility.Utils
@@ -28,11 +34,13 @@ import nya.kitsunyan.foxydroid.utility.extension.text.*
2834
abstract class ScreenActivity: FragmentActivity() {
2935
companion object {
3036
private const val STATE_FRAGMENT_STACK = "fragmentStack"
37+
private const val TAG = "ScreenActivity"
3138
}
3239

3340
sealed class SpecialIntent {
3441
object Updates: SpecialIntent()
3542
object UpdateAll: SpecialIntent() // Added by REV Robotics on 2021-06-07
43+
object PerformActionWaitingOnInternet: SpecialIntent() // Added by REV Robotics on 2021-06-15
3644
class Install(val packageName: String?, val cacheFileName: String?): SpecialIntent()
3745
}
3846

@@ -231,6 +239,31 @@ abstract class ScreenActivity: FragmentActivity() {
231239
val tabsFragment = currentFragment as TabsFragment
232240
tabsFragment.initiateUpdateAll()
233241
}
242+
// SpecialIntent.PerformActionWaitingOnInternet added by REV Robotics on 2021-06-15
243+
is SpecialIntent.PerformActionWaitingOnInternet -> {
244+
Log.i(TAG, "Performing action that was waiting for an Internet connection to be established")
245+
val desiredAction = actionWaitingForInternetConnection
246+
if (desiredAction != null) {
247+
actionWaitingForInternetConnection = null
248+
RequestInternetDialogFragment.instance?.dismiss()
249+
when (desiredAction) {
250+
UpdateAll -> {
251+
handleSpecialIntent(SpecialIntent.UpdateAll)
252+
}
253+
is InstallApk -> {
254+
Connection(DownloadService::class.java, onBind = { connection, _ ->
255+
connection.binder?.enqueue(
256+
desiredAction.packageName,
257+
desiredAction.productName,
258+
desiredAction.repository,
259+
desiredAction.release)
260+
connection.unbind(this)
261+
}).bind(this)
262+
}
263+
}
264+
}
265+
Unit
266+
}
234267
}::class
235268
}
236269

0 commit comments

Comments
 (0)