Move lock screen fragment within pager

This commit is contained in:
Jonas Lochmann 2020-11-02 01:00:00 +01:00
parent 73f1a22ab4
commit 7f207d0c08
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
6 changed files with 151 additions and 134 deletions

View file

@ -23,15 +23,18 @@ import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import io.timelimit.android.R import io.timelimit.android.R
import io.timelimit.android.extensions.showSafe import io.timelimit.android.extensions.showSafe
import io.timelimit.android.logic.BlockingReason
import io.timelimit.android.logic.DefaultAppLogic import io.timelimit.android.logic.DefaultAppLogic
import io.timelimit.android.sync.network.UpdatePrimaryDeviceRequestType
import io.timelimit.android.ui.IsAppInForeground import io.timelimit.android.ui.IsAppInForeground
import io.timelimit.android.ui.login.NewLoginFragment import io.timelimit.android.ui.login.NewLoginFragment
import io.timelimit.android.ui.main.ActivityViewModel import io.timelimit.android.ui.main.ActivityViewModel
import io.timelimit.android.ui.main.ActivityViewModelHolder import io.timelimit.android.ui.main.ActivityViewModelHolder
import io.timelimit.android.ui.manage.child.primarydevice.UpdatePrimaryDeviceDialogFragment
import io.timelimit.android.ui.util.SyncStatusModel import io.timelimit.android.ui.util.SyncStatusModel
import kotlinx.android.synthetic.main.lock_activity.*
class LockActivity : AppCompatActivity(), ActivityViewModelHolder { class LockActivity : AppCompatActivity(), ActivityViewModelHolder {
companion object { companion object {
@ -58,6 +61,9 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder {
} }
private val model: LockModel by viewModels() private val model: LockModel by viewModels()
private val syncModel: SyncStatusModel by viewModels()
private val activityModel: ActivityViewModel by viewModels()
private var isResumed = false
override var ignoreStop: Boolean = false override var ignoreStop: Boolean = false
@ -74,27 +80,30 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.lock_activity)
if (savedInstanceState == null) { setContentView(R.layout.lock_activity)
supportFragmentManager.beginTransaction()
.replace(R.id.container, LockFragment.newInstance(blockedPackageName, blockedActivityName))
.commitNow()
}
if (savedInstanceState == null) { if (savedInstanceState == null) {
stopMediaPlayback() stopMediaPlayback()
} }
val syncModel = ViewModelProviders.of(this).get(SyncStatusModel::class.java) syncModel.statusText.observe(this) { supportActionBar?.subtitle = it }
syncModel.statusText.observe(this, Observer {
supportActionBar?.subtitle = it
})
currentInstances.add(this) currentInstances.add(this)
model.init(blockedPackageName, blockedActivityName) model.init(blockedPackageName, blockedActivityName)
pager.adapter = LockActivityAdapter(supportFragmentManager)
model.content.observe(this) {
if (isResumed && it is LockscreenContent.Blocked.BlockedCategory && it.reason == BlockingReason.RequiresCurrentDevice && !model.didOpenSetCurrentDeviceScreen) {
model.didOpenSetCurrentDeviceScreen = true
UpdatePrimaryDeviceDialogFragment
.newInstance(UpdatePrimaryDeviceRequestType.SetThisDevice)
.show(supportFragmentManager)
}
}
} }
override fun onDestroy() { override fun onDestroy() {
@ -103,9 +112,7 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder {
currentInstances.remove(this) currentInstances.remove(this)
} }
override fun getActivityViewModel(): ActivityViewModel { override fun getActivityViewModel(): ActivityViewModel = activityModel
return ViewModelProviders.of(this).get(ActivityViewModel::class.java)
}
override fun showAuthenticationScreen() { override fun showAuthenticationScreen() {
NewLoginFragment().showSafe(supportFragmentManager, LOGIN_DIALOG_TAG) NewLoginFragment().showSafe(supportFragmentManager, LOGIN_DIALOG_TAG)
@ -115,12 +122,14 @@ class LockActivity : AppCompatActivity(), ActivityViewModelHolder {
super.onResume() super.onResume()
lockTaskModeWorkaround() lockTaskModeWorkaround()
isResumed = true
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
lockTaskModeWorkaround() lockTaskModeWorkaround()
isResumed = false
} }
override fun onStart() { override fun onStart() {

View file

@ -0,0 +1,30 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.timelimit.android.ui.lock
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
class LockActivityAdapter(fragmentManager: FragmentManager): FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount(): Int = 1
override fun getItem(position: Int): Fragment = when (position) {
0 -> LockReasonFragment()
else -> throw IllegalArgumentException()
}
}

View file

@ -54,6 +54,8 @@ class LockModel(application: Application): AndroidViewModel(application) {
val title: String? get() = logic.platformIntegration.getLocalAppTitle(packageAndActivityNameLiveInternal.value!!.first) val title: String? get() = logic.platformIntegration.getLocalAppTitle(packageAndActivityNameLiveInternal.value!!.first)
val icon: Drawable? get() = logic.platformIntegration.getAppIcon(packageAndActivityNameLiveInternal.value!!.first) val icon: Drawable? get() = logic.platformIntegration.getAppIcon(packageAndActivityNameLiveInternal.value!!.first)
val packageAndActivityNameLive: LiveData<Pair<String, String?>> = packageAndActivityNameLiveInternal
var didOpenSetCurrentDeviceScreen = false
fun init(packageName: String, activityName: String?) { fun init(packageName: String, activityName: String?) {
if (didInit) return if (didInit) return
@ -119,18 +121,21 @@ class LockModel(application: Application): AndroidViewModel(application) {
val categoryHandlings = appBaseHandling.categoryIds.map { handlingCache.get(it) } val categoryHandlings = appBaseHandling.categoryIds.map { handlingCache.get(it) }
val blockingHandling = categoryHandlings.find { it.shouldBlockActivities } val blockingHandling = categoryHandlings.find { it.shouldBlockActivities }
value = if (blockingHandling == null) LockscreenContent.Close else LockscreenContent.BlockedCategory( value = if (blockingHandling == null) LockscreenContent.Close else LockscreenContent.Blocked.BlockedCategory(
deviceAndUserRelatedData = deviceAndUserRelatedData, deviceAndUserRelatedData = deviceAndUserRelatedData,
blockingHandling = blockingHandling, blockingHandling = blockingHandling,
level = appBaseHandling.level, level = appBaseHandling.level,
userRelatedData = deviceAndUserRelatedData.userRelatedData, userRelatedData = deviceAndUserRelatedData.userRelatedData,
appBaseHandling = appBaseHandling appPackageName = packageName,
appActivityName = activityName
).also { scheduleUpdate((blockingHandling.dependsOnMaxTime - realTime.timeInMillis)) } ).also { scheduleUpdate((blockingHandling.dependsOnMaxTime - realTime.timeInMillis)) }
} else if (appBaseHandling is AppBaseHandling.BlockDueToNoCategory) { } else if (appBaseHandling is AppBaseHandling.BlockDueToNoCategory) {
value = LockscreenContent.BlockDueToNoCategory( value = LockscreenContent.Blocked.BlockDueToNoCategory(
userRelatedData = deviceAndUserRelatedData.userRelatedData, userRelatedData = deviceAndUserRelatedData.userRelatedData,
deviceId = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.id, deviceId = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.id,
enableActivityLevelBlocking = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.enableActivityLevelBlocking enableActivityLevelBlocking = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.enableActivityLevelBlocking,
appPackageName = packageName,
appActivityName = activityName
) )
} else { } else {
value = LockscreenContent.Close; return value = LockscreenContent.Close; return
@ -201,27 +206,37 @@ class LockModel(application: Application): AndroidViewModel(application) {
sealed class LockscreenContent { sealed class LockscreenContent {
object Close: LockscreenContent() object Close: LockscreenContent()
data class BlockedCategory( sealed class Blocked: LockscreenContent() {
val deviceAndUserRelatedData: DeviceAndUserRelatedData, abstract val userRelatedData: UserRelatedData
val blockingHandling: CategoryItselfHandling, abstract val appPackageName: String
val appBaseHandling: AppBaseHandling, abstract val appActivityName: String?
val level: BlockingLevel, abstract val enableActivityLevelBlocking: Boolean
val userRelatedData: UserRelatedData
): LockscreenContent() {
val appCategoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title
val reason = blockingHandling.activityBlockingReason
val deviceId = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.id
val userId = userRelatedData.user.id
val timeZone = userRelatedData.user.timeZone
val blockedCategoryId = blockingHandling.createdWithCategoryRelatedData.category.id
val deviceRelatedData = deviceAndUserRelatedData.deviceRelatedData
val hasFullVersion = deviceRelatedData.isConnectedAndHasPremium || deviceRelatedData.isLocalMode
val enableActivityLevelBlocking = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.enableActivityLevelBlocking
}
data class BlockDueToNoCategory( class BlockedCategory(
val userRelatedData: UserRelatedData, val deviceAndUserRelatedData: DeviceAndUserRelatedData,
val deviceId: String, val blockingHandling: CategoryItselfHandling,
val enableActivityLevelBlocking: Boolean val level: BlockingLevel,
): LockscreenContent() override val userRelatedData: UserRelatedData,
override val appPackageName: String,
override val appActivityName: String?
): Blocked() {
val appCategoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title
val reason = blockingHandling.activityBlockingReason
val deviceId = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.id
val userId = userRelatedData.user.id
val timeZone = userRelatedData.user.timeZone
val blockedCategoryId = blockingHandling.createdWithCategoryRelatedData.category.id
val deviceRelatedData = deviceAndUserRelatedData.deviceRelatedData
val hasFullVersion = deviceRelatedData.isConnectedAndHasPremium || deviceRelatedData.isLocalMode
override val enableActivityLevelBlocking = deviceAndUserRelatedData.deviceRelatedData.deviceEntry.enableActivityLevelBlocking
}
class BlockDueToNoCategory(
override val userRelatedData: UserRelatedData,
val deviceId: String,
override val enableActivityLevelBlocking: Boolean,
override val appPackageName: String,
override val appActivityName: String?
): Blocked()
}
} }

View file

@ -30,8 +30,8 @@ import io.timelimit.android.data.extensions.sortedCategories
import io.timelimit.android.data.model.* import io.timelimit.android.data.model.*
import io.timelimit.android.data.model.derived.DeviceRelatedData import io.timelimit.android.data.model.derived.DeviceRelatedData
import io.timelimit.android.data.model.derived.UserRelatedData import io.timelimit.android.data.model.derived.UserRelatedData
import io.timelimit.android.databinding.LockFragmentBinding
import io.timelimit.android.databinding.LockFragmentCategoryButtonBinding import io.timelimit.android.databinding.LockFragmentCategoryButtonBinding
import io.timelimit.android.databinding.LockReasonFragmentBinding
import io.timelimit.android.date.DateInTimezone import io.timelimit.android.date.DateInTimezone
import io.timelimit.android.livedata.* import io.timelimit.android.livedata.*
import io.timelimit.android.logic.* import io.timelimit.android.logic.*
@ -53,38 +53,13 @@ import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
import io.timelimit.android.ui.view.SelectTimeSpanViewListener import io.timelimit.android.ui.view.SelectTimeSpanViewListener
import java.util.* import java.util.*
class LockFragment : Fragment() { class LockReasonFragment : Fragment() {
companion object { companion object {
private const val EXTRA_PACKAGE_NAME = "pkg"
private const val EXTRA_ACTIVITY = "activitiy"
private const val STATUS_DID_OPEN_SET_CURRENT_DEVICE_SCREEN = "didOpenSetCurrentDeviceScreen"
private const val LOCATION_REQUEST_CODE = 1 private const val LOCATION_REQUEST_CODE = 1
fun newInstance(packageName: String, activity: String?): LockFragment {
val result = LockFragment()
val arguments = Bundle()
arguments.putString(EXTRA_PACKAGE_NAME, packageName)
if (activity != null) {
arguments.putString(EXTRA_ACTIVITY, activity)
}
result.arguments = arguments
return result
}
} }
private var didOpenSetCurrentDeviceScreen = false
private val packageName: String by lazy { requireArguments().getString(EXTRA_PACKAGE_NAME)!! }
private val activityName: String? by lazy {
if (requireArguments().containsKey(EXTRA_ACTIVITY))
requireArguments().getString(EXTRA_ACTIVITY)
else
null
}
private val auth: ActivityViewModel by lazy { getActivityViewModel(requireActivity()) } private val auth: ActivityViewModel by lazy { getActivityViewModel(requireActivity()) }
private lateinit var binding: LockFragmentBinding private lateinit var binding: LockReasonFragmentBinding
private val model: LockModel by activityViewModels() private val model: LockModel by activityViewModels()
private fun setupHandlers(deviceId: String, userRelatedData: UserRelatedData, blockedCategoryId: String?) { private fun setupHandlers(deviceId: String, userRelatedData: UserRelatedData, blockedCategoryId: String?) {
@ -140,23 +115,23 @@ class LockFragment : Fragment() {
(activity as LockActivity).showAuthenticationScreen() (activity as LockActivity).showAuthenticationScreen()
} }
override fun setThisDeviceAsCurrentDevice() = this@LockFragment.setThisDeviceAsCurrentDevice() override fun setThisDeviceAsCurrentDevice() = this@LockReasonFragment.setThisDeviceAsCurrentDevice()
override fun requestLocationPermission() { override fun requestLocationPermission() {
RequestWifiPermission.doRequest(this@LockFragment, LOCATION_REQUEST_CODE) RequestWifiPermission.doRequest(this@LockReasonFragment, LOCATION_REQUEST_CODE)
} }
} }
} }
private fun setThisDeviceAsCurrentDevice() { private fun setThisDeviceAsCurrentDevice() {
didOpenSetCurrentDeviceScreen = true model.didOpenSetCurrentDeviceScreen = true
UpdatePrimaryDeviceDialogFragment UpdatePrimaryDeviceDialogFragment
.newInstance(UpdatePrimaryDeviceRequestType.SetThisDevice) .newInstance(UpdatePrimaryDeviceRequestType.SetThisDevice)
.show(parentFragmentManager) .show(parentFragmentManager)
} }
private fun bindAddToCategoryOptions(userRelatedData: UserRelatedData) { private fun bindAddToCategoryOptions(userRelatedData: UserRelatedData, blockedPackageName: String) {
binding.addToCategoryOptions.removeAllViews() binding.addToCategoryOptions.removeAllViews()
userRelatedData.sortedCategories().forEach { (_, category) -> userRelatedData.sortedCategories().forEach { (_, category) ->
@ -166,7 +141,7 @@ class LockFragment : Fragment() {
auth.tryDispatchParentAction( auth.tryDispatchParentAction(
AddCategoryAppsAction( AddCategoryAppsAction(
categoryId = category.category.id, categoryId = category.category.id,
packageNames = listOf(packageName) packageNames = listOf(blockedPackageName)
) )
) )
} }
@ -238,22 +213,8 @@ class LockFragment : Fragment() {
model.missingNetworkIdPermission.observe(viewLifecycleOwner) { binding.missingNetworkIdPermission = it } model.missingNetworkIdPermission.observe(viewLifecycleOwner) { binding.missingNetworkIdPermission = it }
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
didOpenSetCurrentDeviceScreen = savedInstanceState.getBoolean(STATUS_DID_OPEN_SET_CURRENT_DEVICE_SCREEN)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(STATUS_DID_OPEN_SET_CURRENT_DEVICE_SCREEN, didOpenSetCurrentDeviceScreen)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = LockFragmentBinding.inflate(layoutInflater, container, false) binding = LockReasonFragmentBinding.inflate(layoutInflater, container, false)
AuthenticationFab.manageAuthenticationFab( AuthenticationFab.manageAuthenticationFab(
fab = binding.fab, fab = binding.fab,
@ -272,7 +233,7 @@ class LockFragment : Fragment() {
) )
} }
binding.packageName = packageName model.packageAndActivityNameLive.observe(viewLifecycleOwner) { binding.packageName = it.first }
binding.appTitle = model.title ?: "???" binding.appTitle = model.title ?: "???"
binding.appIcon.setImageDrawable(model.icon) binding.appIcon.setImageDrawable(model.icon)
@ -288,50 +249,51 @@ class LockFragment : Fragment() {
requireActivity().finish() requireActivity().finish()
} }
is LockscreenContent.BlockedCategory -> { is LockscreenContent.Blocked -> {
binding.activityName = if (content.enableActivityLevelBlocking) activityName?.removePrefix(packageName) else null binding.activityName = if (content.enableActivityLevelBlocking) content.appActivityName?.removePrefix(content.appPackageName) else null
binding.appCategoryTitle = content.appCategoryTitle when (content) {
binding.reason = content.reason is LockscreenContent.Blocked.BlockedCategory -> {
binding.blockedKindLabel = when (content.level) { binding.appCategoryTitle = content.appCategoryTitle
BlockingLevel.Activity -> "Activity" binding.reason = content.reason
BlockingLevel.App -> "App" binding.blockedKindLabel = when (content.level) {
} BlockingLevel.Activity -> "Activity"
setupHandlers( BlockingLevel.App -> "App"
deviceId = content.deviceId, }
blockedCategoryId = content.blockedCategoryId, setupHandlers(
userRelatedData = content.userRelatedData deviceId = content.deviceId,
) blockedCategoryId = content.blockedCategoryId,
bindExtraTimeView( userRelatedData = content.userRelatedData
deviceRelatedData = content.deviceRelatedData, )
categoryId = content.blockedCategoryId, bindExtraTimeView(
timeZone = content.userRelatedData.timeZone deviceRelatedData = content.deviceRelatedData,
) categoryId = content.blockedCategoryId,
binding.manageDisableTimeLimits.handlers = ManageDisableTimelimitsViewHelper.createHandlers( timeZone = content.userRelatedData.timeZone
childId = content.userId, )
childTimezone = content.timeZone, binding.manageDisableTimeLimits.handlers = ManageDisableTimelimitsViewHelper.createHandlers(
activity = requireActivity(), childId = content.userId,
hasFullVersion = content.hasFullVersion childTimezone = content.timeZone,
) activity = requireActivity(),
hasFullVersion = content.hasFullVersion
)
}
is LockscreenContent.Blocked.BlockDueToNoCategory -> {
binding.appCategoryTitle = null
binding.reason = BlockingReason.NotPartOfAnCategory
binding.blockedKindLabel = "App"
if (content.reason == BlockingReason.RequiresCurrentDevice && !didOpenSetCurrentDeviceScreen && isResumed) { setupHandlers(
setThisDeviceAsCurrentDevice() deviceId = content.deviceId,
} else null blockedCategoryId = null,
} userRelatedData = content.userRelatedData
is LockscreenContent.BlockDueToNoCategory -> { )
binding.activityName = if (content.enableActivityLevelBlocking) activityName?.removePrefix(packageName) else null
binding.appCategoryTitle = null bindAddToCategoryOptions(
binding.reason = BlockingReason.NotPartOfAnCategory userRelatedData = content.userRelatedData,
binding.blockedKindLabel = "App" blockedPackageName = content.appPackageName
)
setupHandlers( }
deviceId = content.deviceId, }.let {/* require handling all paths */}
blockedCategoryId = null,
userRelatedData = content.userRelatedData
)
bindAddToCategoryOptions(content.userRelatedData)
} }
}.let {/* require handling all paths */} }.let {/* require handling all paths */}
} }

View file

@ -13,9 +13,10 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/container" android:id="@+id/pager"
tools:context=".ui.lock.LockActivity" /> tools:context=".ui.lock.LockActivity" />

View file

@ -16,7 +16,7 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.lock.LockFragment"> tools:context=".ui.lock.LockReasonFragment">
<data> <data>
<variable <variable