• 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.launcher3.taskbar.customization
18 
19 import android.annotation.SuppressLint
20 import android.content.Context
21 import android.content.res.ColorStateList
22 import android.graphics.Color.TRANSPARENT
23 import android.util.AttributeSet
24 import android.view.MotionEvent
25 import android.view.View
26 import android.view.ViewConfiguration
27 import androidx.annotation.DimenRes
28 import androidx.annotation.DrawableRes
29 import androidx.core.view.setPadding
30 import com.android.launcher3.R
31 import com.android.launcher3.Utilities.dpToPx
32 import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
33 import com.android.launcher3.taskbar.TaskbarActivityContext
34 import com.android.launcher3.taskbar.TaskbarViewCallbacks
35 import com.android.launcher3.util.Executors.MAIN_EXECUTOR
36 import com.android.launcher3.views.ActivityContext
37 import com.android.launcher3.views.IconButtonView
38 import com.android.quickstep.DeviceConfigWrapper
39 import com.android.quickstep.util.ContextualSearchStateManager
40 import com.android.wm.shell.Flags
41 
42 /** Taskbar all apps button container for customizable taskbar. */
43 class TaskbarAllAppsButtonContainer
44 @JvmOverloads
45 constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
46     IconButtonView(context, attrs), TaskbarContainer {
47 
48     private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
49     private var allAppsTouchTriggered = false
50     private var allAppsTouchRunnable: Runnable? = null
51     private var allAppsButtonTouchDelayMs: Long = ViewConfiguration.getLongPressTimeout().toLong()
52     private lateinit var taskbarViewCallbacks: TaskbarViewCallbacks
53 
54     override val spaceNeeded: Int
55         get() {
56             return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat())
57         }
58 
59     init {
60         contentDescription = context.getString(R.string.all_apps_button_label)
61         setUpIcon()
62     }
63 
64     @SuppressLint("UseCompatLoadingForDrawables", "ResourceAsColor")
setUpIconnull65     private fun setUpIcon() {
66         val drawable =
67             resources.getDrawable(
68                 getAllAppsButton(activityContext.taskbarFeatureEvaluator.isTransient)
69             )
70         backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
71         setIconDrawable(drawable)
72         if (!activityContext.isTransientTaskbar) {
73             setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
74         }
75         setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
76     }
77 
78     @SuppressLint("ClickableViewAccessibility")
setUpCallbacksnull79     fun setUpCallbacks(callbacks: TaskbarViewCallbacks) {
80         taskbarViewCallbacks = callbacks
81         setOnClickListener(this::onAllAppsButtonClick)
82         setOnLongClickListener(this::onAllAppsButtonLongClick)
83         setOnTouchListener(this::onAllAppsButtonTouch)
84         isHapticFeedbackEnabled =
85             taskbarViewCallbacks.isAllAppsButtonHapticFeedbackEnabled(mContext)
86         allAppsTouchRunnable = Runnable {
87             taskbarViewCallbacks.triggerAllAppsButtonLongClick()
88             allAppsTouchTriggered = true
89         }
90         val contextualSearchStateManager = ContextualSearchStateManager.INSTANCE[mContext]
91         if (
92             DeviceConfigWrapper.get().customLpaaThresholds &&
93                 contextualSearchStateManager.lpnhDurationMillis.isPresent
94         ) {
95             allAppsButtonTouchDelayMs = contextualSearchStateManager.lpnhDurationMillis.get()
96         }
97     }
98 
99     @DrawableRes
getAllAppsButtonnull100     private fun getAllAppsButton(isTransientTaskbar: Boolean): Int {
101         if (Flags.enableGsf()) {
102             return getAllAppsButtonForExpressiveTheme()
103         }
104         val shouldSelectTransientIcon =
105             isTransientTaskbar || (enableTaskbarPinning() && !activityContext.isThreeButtonNav)
106         return if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button
107         else R.drawable.ic_taskbar_all_apps_search_button
108     }
109 
110     @DrawableRes
getAllAppsButtonForExpressiveThemenull111     private fun getAllAppsButtonForExpressiveTheme(): Int {
112         return R.drawable.ic_taskbar_all_apps_search_button_expressive_theme
113     }
114 
115     @DimenRes
getAllAppsButtonTranslationXOffsetForExpressiveThemenull116     fun getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar: Boolean): Int {
117         return if (isTransientTaskbar) {
118             R.dimen.transient_taskbar_all_apps_button_translation_x_offset_for_expressive_theme
119         } else {
120             R.dimen.taskbar_all_apps_search_button_translation_x_offset_for_expressive_theme
121         }
122     }
123 
124     @DimenRes
getAllAppsButtonTranslationXOffsetnull125     fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
126         if (Flags.enableGsf()) {
127             return getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar)
128         }
129         return if (isTransientTaskbar) {
130             R.dimen.transient_taskbar_all_apps_button_translation_x_offset
131         } else {
132             R.dimen.taskbar_all_apps_search_button_translation_x_offset
133         }
134     }
135 
onAllAppsButtonTouchnull136     private fun onAllAppsButtonTouch(view: View, ev: MotionEvent): Boolean {
137         when (ev.action) {
138             MotionEvent.ACTION_DOWN -> {
139                 allAppsTouchTriggered = false
140                 MAIN_EXECUTOR.handler.postDelayed(allAppsTouchRunnable!!, allAppsButtonTouchDelayMs)
141             }
142             MotionEvent.ACTION_UP,
143             MotionEvent.ACTION_CANCEL -> cancelAllAppsButtonTouch()
144         }
145         return false
146     }
147 
cancelAllAppsButtonTouchnull148     private fun cancelAllAppsButtonTouch() {
149         MAIN_EXECUTOR.handler.removeCallbacks(allAppsTouchRunnable!!)
150         // ACTION_UP is first triggered, then click listener / long-click listener is triggered on
151         // the next frame, so we need to post twice and delay the reset.
152         this.post { this.post { allAppsTouchTriggered = false } }
153     }
154 
onAllAppsButtonClicknull155     private fun onAllAppsButtonClick(view: View) {
156         if (!allAppsTouchTriggered) {
157             taskbarViewCallbacks.triggerAllAppsButtonClick(view)
158         }
159     }
160 
161     // Handle long click from Switch Access and Voice Access
onAllAppsButtonLongClicknull162     private fun onAllAppsButtonLongClick(view: View): Boolean {
163         if (!MAIN_EXECUTOR.handler.hasCallbacks(allAppsTouchRunnable!!) && !allAppsTouchTriggered) {
164             taskbarViewCallbacks.triggerAllAppsButtonLongClick()
165         }
166         return true
167     }
168 }
169