AndroidAPS/app/src/main/java/info/nightscout/androidaps/MainActivity.kt

401 lines
19 KiB
Kotlin
Raw Normal View History

2020-04-01 21:10:48 +02:00
package info.nightscout.androidaps
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.os.PersistableBundle
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.util.Linkify
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
2020-04-25 21:52:20 +02:00
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
2021-01-08 18:55:01 +01:00
import com.google.firebase.crashlytics.FirebaseCrashlytics
2020-04-01 21:10:48 +02:00
import com.joanzapata.iconify.Iconify
import com.joanzapata.iconify.fonts.FontAwesomeModule
2020-11-28 17:26:03 +01:00
import dev.doubledot.doki.ui.DokiActivity
2021-05-23 21:38:30 +02:00
import info.nightscout.androidaps.activities.*
2021-04-10 11:35:38 +02:00
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
2021-01-22 11:34:28 +01:00
import info.nightscout.androidaps.databinding.ActivityMainBinding
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
2021-11-30 17:08:31 +01:00
import info.nightscout.androidaps.interfaces.*
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
2021-04-10 11:35:38 +02:00
import info.nightscout.androidaps.logging.UserEntryLogger
2021-10-15 14:56:22 +02:00
import info.nightscout.androidaps.plugins.bus.RxBus
2020-05-04 01:27:47 +02:00
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.FabricPrivacy
2020-04-20 01:18:10 +02:00
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest
2020-07-06 21:14:00 +02:00
import info.nightscout.androidaps.utils.locale.LocaleHelper
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.utils.protection.ProtectionCheck
2021-02-04 20:54:09 +01:00
import info.nightscout.androidaps.utils.rx.AapsSchedulers
2020-04-01 21:10:48 +02:00
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.tabs.TabPageAdapter
import info.nightscout.androidaps.utils.ui.UIRunnable
2020-04-01 21:10:48 +02:00
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
2020-05-04 01:27:47 +02:00
import java.util.*
2020-04-01 21:10:48 +02:00
import javax.inject.Inject
import kotlin.system.exitProcess
class MainActivity : NoSplashAppCompatActivity() {
2020-12-06 13:19:53 +01:00
2020-04-01 21:10:48 +02:00
private val disposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger
2021-02-04 20:54:09 +01:00
@Inject lateinit var aapsSchedulers: AapsSchedulers
2021-10-15 14:56:22 +02:00
@Inject lateinit var rxBus: RxBus
2020-04-01 21:10:48 +02:00
@Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
2021-11-30 17:08:31 +01:00
@Inject lateinit var loop: Loop
2020-04-01 21:10:48 +02:00
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
@Inject lateinit var buildHelper: BuildHelper
2021-04-14 00:45:30 +02:00
@Inject lateinit var activePlugin: ActivePlugin
2020-04-01 21:10:48 +02:00
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var protectionCheck: ProtectionCheck
2020-05-03 17:50:33 +02:00
@Inject lateinit var iconsProvider: IconsProvider
2020-05-04 01:27:47 +02:00
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
2020-05-07 09:54:36 +02:00
@Inject lateinit var config: Config
2021-04-10 11:35:38 +02:00
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var profileFunction: ProfileFunction
2020-04-01 21:10:48 +02:00
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null
2020-04-01 21:10:48 +02:00
2021-01-22 11:34:28 +01:00
private lateinit var binding: ActivityMainBinding
2020-04-01 21:10:48 +02:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Iconify.with(FontAwesomeModule())
2020-04-25 21:52:20 +02:00
LocaleHelper.update(applicationContext)
2021-01-22 11:34:28 +01:00
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
2020-04-01 21:10:48 +02:00
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
2021-01-22 11:34:28 +01:00
actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.mainDrawerLayout, R.string.open_navigation, R.string.close_navigation).also {
binding.mainDrawerLayout.addDrawerListener(it)
2020-04-01 21:10:48 +02:00
it.syncState()
}
// initialize screen wake lock
2021-11-02 09:53:16 +01:00
processPreferenceChange(EventPreferenceChange(rh.gs(R.string.key_keep_screen_on)))
2021-01-22 11:34:28 +01:00
binding.mainPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
2020-04-25 21:52:20 +02:00
override fun onPageScrollStateChanged(state: Int) {}
2020-04-01 21:10:48 +02:00
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
setPluginPreferenceMenuName()
2021-01-22 11:34:28 +01:00
checkPluginPreferences(binding.mainPager)
2020-04-01 21:10:48 +02:00
}
})
//Check here if loop plugin is disabled. Else check via constraints
2021-11-30 17:08:31 +01:00
if (!(loop as PluginBase).isEnabled()) versionCheckerUtils.triggerCheckVersion()
2020-05-04 01:27:47 +02:00
setUserStats()
2020-04-01 21:10:48 +02:00
setupViews()
disposable += rxBus
2020-04-01 21:10:48 +02:00
.toObservable(EventRebuildTabs::class.java)
2021-02-04 20:54:09 +01:00
.observeOn(aapsSchedulers.main)
2020-04-01 21:10:48 +02:00
.subscribe({
if (it.recreate) recreate()
else setupViews()
setWakeLock()
}, fabricPrivacy::logException)
disposable += rxBus
2020-04-01 21:10:48 +02:00
.toObservable(EventPreferenceChange::class.java)
2021-02-04 20:54:09 +01:00
.observeOn(aapsSchedulers.main)
2020-12-06 13:19:53 +01:00
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
2021-02-21 12:30:45 +01:00
if (startWizard() && !isRunningRealPumpTest()) {
2020-12-06 13:19:53 +01:00
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
})
2020-04-01 21:10:48 +02:00
}
androidPermission.notifyForStoragePermission(this)
androidPermission.notifyForBatteryOptimizationPermission(this)
if (!config.NSCLIENT) androidPermission.notifyForLocationPermissions(this)
2020-05-07 09:54:36 +02:00
if (config.PUMPDRIVERS) {
2020-04-01 21:10:48 +02:00
androidPermission.notifyForSMSPermissions(this, smsCommunicatorPlugin)
androidPermission.notifyForSystemWindowPermissions(this)
}
}
2020-04-25 21:52:20 +02:00
private fun checkPluginPreferences(viewPager: ViewPager2) {
if (viewPager.currentItem >= 0) pluginPreferencesMenuItem?.isEnabled = (viewPager.adapter as TabPageAdapter).getPluginAt(viewPager.currentItem).preferencesId != -1
2020-04-01 21:10:48 +02:00
}
private fun startWizard(): Boolean =
2021-02-21 12:30:45 +01:00
!sp.getBoolean(R.string.key_setupwizard_processed, false)
2020-04-01 21:10:48 +02:00
override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onPostCreate(savedInstanceState, persistentState)
actionBarDrawerToggle.syncState()
}
2021-10-01 11:07:16 +02:00
override fun onDestroy() {
2020-04-01 21:10:48 +02:00
super.onDestroy()
disposable.clear()
}
override fun onResume() {
super.onResume()
2020-04-20 01:18:10 +02:00
protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null,
2021-11-02 09:53:16 +01:00
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } },
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } }
)
2020-04-01 21:10:48 +02:00
}
private fun setWakeLock() {
val keepScreenOn = sp.getBoolean(R.string.key_keep_screen_on, false)
if (keepScreenOn) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun processPreferenceChange(ev: EventPreferenceChange) {
2021-11-02 09:53:16 +01:00
if (ev.isChanged(rh, R.string.key_keep_screen_on)) setWakeLock()
if (ev.isChanged(rh, R.string.key_skin)) recreate()
2020-04-01 21:10:48 +02:00
}
private fun setupViews() {
2020-04-25 21:52:20 +02:00
// Menu
val pageAdapter = TabPageAdapter(this)
2021-01-22 11:34:28 +01:00
binding.mainNavigationView.setNavigationItemSelectedListener { true }
val menu = binding.mainNavigationView.menu.also { it.clear() }
for (p in activePlugin.getPluginsList()) {
2020-04-01 21:10:48 +02:00
pageAdapter.registerNewFragment(p)
2020-04-25 21:52:20 +02:00
if (p.isEnabled() && p.hasFragment() && !p.isFragmentVisible() && !p.pluginDescription.neverVisible) {
2020-04-01 21:10:48 +02:00
val menuItem = menu.add(p.name)
menuItem.isCheckable = true
if (p.menuIcon != -1) {
menuItem.setIcon(p.menuIcon)
} else {
menuItem.setIcon(R.drawable.ic_settings)
}
2020-04-01 21:10:48 +02:00
menuItem.setOnMenuItemClickListener {
val intent = Intent(this, SingleFragmentActivity::class.java)
intent.putExtra("plugin", activePlugin.getPluginsList().indexOf(p))
2020-04-01 21:10:48 +02:00
startActivity(intent)
2021-01-22 11:34:28 +01:00
binding.mainDrawerLayout.closeDrawers()
2020-04-01 21:10:48 +02:00
true
}
}
}
2021-01-22 11:34:28 +01:00
binding.mainPager.adapter = pageAdapter
binding.mainPager.offscreenPageLimit = 8 // This may cause more memory consumption
checkPluginPreferences(binding.mainPager)
2020-04-01 21:10:48 +02:00
2020-04-25 21:52:20 +02:00
// Tabs
2020-04-01 21:10:48 +02:00
if (sp.getBoolean(R.string.key_short_tabtitles, false)) {
2021-01-22 11:34:28 +01:00
binding.tabsNormal.visibility = View.GONE
binding.tabsCompact.visibility = View.VISIBLE
binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt())
TabLayoutMediator(binding.tabsCompact, binding.mainPager) { tab, position ->
tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).nameShort
2020-04-25 21:52:20 +02:00
}.attach()
2020-04-01 21:10:48 +02:00
} else {
2021-01-22 11:34:28 +01:00
binding.tabsNormal.visibility = View.VISIBLE
binding.tabsCompact.visibility = View.GONE
2020-04-01 21:10:48 +02:00
val typedValue = TypedValue()
if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) {
binding.toolbar.layoutParams = LinearLayout.LayoutParams(
Toolbar.LayoutParams.MATCH_PARENT,
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
)
2020-04-01 21:10:48 +02:00
}
2021-01-22 11:34:28 +01:00
TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position ->
tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name
2020-04-25 21:52:20 +02:00
}.attach()
2020-04-01 21:10:48 +02:00
}
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val v = currentFocus
if (v is EditText) {
val outRect = Rect()
v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus()
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.getWindowToken(), 0)
}
}
}
return super.dispatchTouchEvent(event)
}
private fun setPluginPreferenceMenuName() {
2021-01-22 11:34:28 +01:00
if (binding.mainPager.currentItem >= 0) {
val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem)
2021-11-02 09:53:16 +01:00
this.menu?.findItem(R.id.nav_plugin_preferences)?.title = rh.gs(R.string.nav_preferences_plugin, plugin.name)
2021-01-07 11:16:41 +01:00
}
}
override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
val result = super.onMenuOpened(featureId, menu)
menu.findItem(R.id.nav_treatments)?.isEnabled = profileFunction.getProfile() != null
return result
}
2020-04-01 21:10:48 +02:00
override fun onCreateOptionsMenu(menu: Menu): Boolean {
this.menu = menu
2020-04-01 21:10:48 +02:00
menuInflater.inflate(R.menu.menu_main, menu)
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)
setPluginPreferenceMenuName()
2021-01-22 11:34:28 +01:00
checkPluginPreferences(binding.mainPager)
2020-04-01 21:10:48 +02:00
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.nav_preferences -> {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
2020-04-01 21:10:48 +02:00
val i = Intent(this, PreferencesActivity::class.java)
i.putExtra("id", -1)
startActivity(i)
})
return true
}
R.id.nav_historybrowser -> {
2020-04-01 21:10:48 +02:00
startActivity(Intent(this, HistoryBrowseActivity::class.java))
return true
}
R.id.nav_treatments -> {
2021-05-23 21:38:30 +02:00
startActivity(Intent(this, TreatmentsActivity::class.java))
return true
}
R.id.nav_setupwizard -> {
2020-12-06 13:19:53 +01:00
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
})
2020-04-01 21:10:48 +02:00
return true
}
R.id.nav_about -> {
2020-04-01 21:10:48 +02:00
var message = "Build: ${BuildConfig.BUILDVERSION}\n"
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
2021-11-02 09:53:16 +01:00
message += "${rh.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (buildHelper.isEngineeringMode()) message += "\n${rh.gs(R.string.engineering_mode_enabled)}"
if (!fabricPrivacy.fabricEnabled()) message += "\n${rh.gs(R.string.fabric_upload_disabled)}"
message += rh.gs(R.string.about_link_urls)
2020-04-01 21:10:48 +02:00
val messageSpanned = SpannableString(message)
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS)
AlertDialog.Builder(this)
2021-11-02 09:53:16 +01:00
.setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION)
2020-05-03 17:50:33 +02:00
.setIcon(iconsProvider.getIcon())
2020-04-01 21:10:48 +02:00
.setMessage(messageSpanned)
2021-11-02 09:53:16 +01:00
.setPositiveButton(rh.gs(R.string.ok), null)
.setNeutralButton(rh.gs(R.string.cta_dont_kill_my_app_info)) { _, _ -> DokiActivity.start(context = this@MainActivity) }
2020-11-28 17:26:03 +01:00
.create().apply {
show()
findViewById<TextView>(android.R.id.message)?.movementMethod = LinkMovementMethod.getInstance()
2020-04-01 21:10:48 +02:00
}
return true
}
R.id.nav_exit -> {
2020-04-01 21:10:48 +02:00
aapsLogger.debug(LTag.CORE, "Exiting")
2021-04-10 11:35:38 +02:00
uel.log(Action.EXIT_AAPS, Sources.Aaps)
2020-04-01 21:10:48 +02:00
rxBus.send(EventAppExit())
finish()
System.runFinalization()
exitProcess(0)
}
R.id.nav_plugin_preferences -> {
2021-01-22 11:34:28 +01:00
val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem)
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
2020-04-01 21:10:48 +02:00
val i = Intent(this, PreferencesActivity::class.java)
i.putExtra("id", plugin.preferencesId)
startActivity(i)
})
return true
}
/*
R.id.nav_survey -> {
startActivity(Intent(this, SurveyActivity::class.java))
return true
}
*/
R.id.nav_defaultprofile -> {
2020-07-06 21:14:00 +02:00
startActivity(Intent(this, ProfileHelperActivity::class.java))
return true
}
R.id.nav_stats -> {
2020-04-01 21:10:48 +02:00
startActivity(Intent(this, StatsActivity::class.java))
return true
}
}
return actionBarDrawerToggle.onOptionsItemSelected(item)
}
2020-05-04 01:27:47 +02:00
// Correct place for calling setUserStats() would be probably MainApp
// but we need to have it called at least once a day. Thus this location
private fun setUserStats() {
if (!fabricPrivacy.fabricEnabled()) return
val closedLoopEnabled = if (constraintChecker.isClosedLoopAllowed().value()) "CLOSED_LOOP_ENABLED" else "CLOSED_LOOP_DISABLED"
// Size is limited to 36 chars
2021-05-18 22:31:08 +02:00
val remote = BuildConfig.REMOTE.lowercase(Locale.getDefault())
2020-05-04 01:27:47 +02:00
.replace("https://", "")
.replace("http://", "")
.replace(".git", "")
.replace(".com/", ":")
.replace(".org/", ":")
.replace(".net/", ":")
fabricPrivacy.firebaseAnalytics.setUserProperty("Mode", BuildConfig.APPLICATION_ID + "-" + closedLoopEnabled)
fabricPrivacy.firebaseAnalytics.setUserProperty("Language", sp.getString(R.string.key_language, Locale.getDefault().language))
fabricPrivacy.firebaseAnalytics.setUserProperty("Version", BuildConfig.VERSION)
fabricPrivacy.firebaseAnalytics.setUserProperty("HEAD", BuildConfig.HEAD)
fabricPrivacy.firebaseAnalytics.setUserProperty("Remote", remote)
val hashes: List<String> = signatureVerifierPlugin.shortHashes()
if (hashes.isNotEmpty()) fabricPrivacy.firebaseAnalytics.setUserProperty("Hash", hashes[0])
activePlugin.activePump.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Pump", it::class.java.simpleName) }
2020-05-07 09:54:36 +02:00
if (!config.NSCLIENT && !config.PUMPCONTROL)
2020-05-04 01:27:47 +02:00
activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) }
activePlugin.activeBgSource.let { fabricPrivacy.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) }
2021-04-14 22:58:21 +02:00
fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileSource.javaClass.simpleName)
2020-05-04 01:27:47 +02:00
activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) }
activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) }
2021-01-08 18:55:01 +01:00
// Add to crash log too
FirebaseCrashlytics.getInstance().setCustomKey("HEAD", BuildConfig.HEAD)
FirebaseCrashlytics.getInstance().setCustomKey("Version", BuildConfig.VERSION)
FirebaseCrashlytics.getInstance().setCustomKey("Remote", remote)
2021-01-22 11:34:28 +01:00
FirebaseCrashlytics.getInstance().setCustomKey("Committed", BuildConfig.COMMITTED)
2021-01-08 18:55:01 +01:00
FirebaseCrashlytics.getInstance().setCustomKey("Hash", hashes[0])
2021-02-21 12:30:45 +01:00
FirebaseCrashlytics.getInstance().setCustomKey("Email", sp.getString(R.string.key_email_for_crash_report, ""))
2020-05-04 01:27:47 +02:00
}
}