• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.desktopmode.education
18 
19 import android.annotation.IntegerRes
20 import android.app.ActivityManager.RunningTaskInfo
21 import android.content.Context
22 import android.os.SystemClock
23 import android.provider.Settings.Secure
24 import com.android.wm.shell.R
25 import com.android.wm.shell.desktopmode.CaptionState
26 import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository
27 import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
28 import java.time.Duration
29 
30 /** Filters incoming App-to-Web education triggers based on set conditions. */
31 class AppToWebEducationFilter(
32     private val context: Context,
33     private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository,
34 ) {
35 
36     /** Returns true if conditions to show App-to-web education are met, returns false otherwise. */
shouldShowAppToWebEducationnull37     suspend fun shouldShowAppToWebEducation(captionState: CaptionState): Boolean {
38         val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) =
39             when (captionState) {
40                 is CaptionState.AppHandle ->
41                     Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable)
42                 is CaptionState.AppHeader ->
43                     Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable)
44                 else -> return false
45             }
46 
47         val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false
48         val windowingEducationProto = appToWebEducationDatastoreRepository.windowingEducationProto()
49 
50         return !isOtherEducationShowing() &&
51             !isEducationViewLimitReached(windowingEducationProto) &&
52             hasSufficientTimeSinceSetup() &&
53             !isFeatureUsedBefore(windowingEducationProto) &&
54             isCapturedLinkAvailable &&
55             isFocusAppInAllowlist(focusAppPackageName)
56     }
57 
isFocusAppInAllowlistnull58     private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean =
59         focusAppPackageName in
60             context.resources.getStringArray(
61                 R.array.desktop_windowing_app_to_web_education_allowlist_apps
62             )
63 
64     // TODO: b/350953004 - Add checks based on App compat
65     // TODO: b/350951797 - Add checks based on PKT tips education
66     private fun isOtherEducationShowing(): Boolean =
67         isTaskbarEducationShowing() || isCompatUiEducationShowing()
68 
69     private fun isTaskbarEducationShowing(): Boolean =
70         Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1
71 
72     private fun isCompatUiEducationShowing(): Boolean =
73         Secure.getInt(context.contentResolver, Secure.COMPAT_UI_EDUCATION_SHOWING, 0) == 1
74 
75     private fun hasSufficientTimeSinceSetup(): Boolean =
76         Duration.ofMillis(SystemClock.elapsedRealtime()) >
77             convertIntegerResourceToDuration(
78                 R.integer.desktop_windowing_education_required_time_since_setup_seconds
79             )
80 
81     /** Returns true if education is viewed maximum amount of times it should be shown. */
82     fun isEducationViewLimitReached(windowingEducationProto: WindowingEducationProto): Boolean =
83         windowingEducationProto.getAppToWebEducation().getEducationShownCount() >=
84             MAXIMUM_TIMES_EDUCATION_SHOWN
85 
86     private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
87         windowingEducationProto.hasFeatureUsedTimestampMillis()
88 
89     private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration =
90         Duration.ofSeconds(context.resources.getInteger(resourceId).toLong())
91 
92     companion object {
93         private const val MAXIMUM_TIMES_EDUCATION_SHOWN = 100
94     }
95 }
96