• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.wm.shell.bubbles
17 
18 import android.content.Context
19 import android.graphics.Color
20 import android.graphics.PointF
21 import android.view.LayoutInflater
22 import android.view.View
23 import android.widget.LinearLayout
24 import android.widget.TextView
25 import com.android.internal.util.ContrastColorUtil
26 import com.android.wm.shell.R
27 import com.android.wm.shell.animation.Interpolators
28 
29 /**
30  * User education view to highlight the collapsed stack of bubbles.
31  * Shown only the first time a user taps the stack.
32  */
33 class StackEducationView constructor(context: Context) : LinearLayout(context) {
34 
35     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
36         else BubbleDebugConfig.TAG_BUBBLES
37 
38     private val ANIMATE_DURATION: Long = 200
39     private val ANIMATE_DURATION_SHORT: Long = 40
40 
<lambda>null41     private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
<lambda>null42     private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
<lambda>null43     private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) }
44 
45     private var isHiding = false
46 
47     init {
48         LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
49 
50         visibility = View.GONE
51         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
52 
53         // BubbleStackView forces LTR by default
54         // since most of Bubble UI direction depends on positioning by the user.
55         // This view actually lays out differently in RTL, so we set layout LOCALE here.
56         layoutDirection = View.LAYOUT_DIRECTION_LOCALE
57     }
58 
setLayoutDirectionnull59     override fun setLayoutDirection(layoutDirection: Int) {
60         super.setLayoutDirection(layoutDirection)
61         setDrawableDirection()
62     }
63 
onFinishInflatenull64     override fun onFinishInflate() {
65         super.onFinishInflate()
66         layoutDirection = resources.configuration.layoutDirection
67         setTextColor()
68     }
69 
setTextColornull70     private fun setTextColor() {
71         val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
72             android.R.attr.textColorPrimaryInverse))
73         val bgColor = ta.getColor(0 /* index */, Color.BLACK)
74         var textColor = ta.getColor(1 /* index */, Color.WHITE)
75         ta.recycle()
76         textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
77         titleTextView.setTextColor(textColor)
78         descTextView.setTextColor(textColor)
79     }
80 
setDrawableDirectionnull81     private fun setDrawableDirection() {
82         view.setBackgroundResource(
83             if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR)
84                 R.drawable.bubble_stack_user_education_bg
85             else R.drawable.bubble_stack_user_education_bg_rtl)
86     }
87 
88     /**
89      * If necessary, shows the user education view for the bubble stack. This appears the first
90      * time a user taps on a bubble.
91      *
92      * @return true if user education was shown, false otherwise.
93      */
shownull94     fun show(stackPosition: PointF): Boolean {
95         if (visibility == VISIBLE) return false
96 
97         setAlpha(0f)
98         setVisibility(View.VISIBLE)
99         post {
100             with(view) {
101                 val bubbleSize = context.resources.getDimensionPixelSize(
102                     R.dimen.bubble_size)
103                 translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2
104             }
105             animate()
106                 .setDuration(ANIMATE_DURATION)
107                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
108                 .alpha(1f)
109         }
110         setShouldShow(false)
111         return true
112     }
113 
114     /**
115      * If necessary, hides the stack education view.
116      *
117      * @param fromExpansion if true this indicates the hide is happening due to the bubble being
118      *                      expanded, false if due to a touch outside of the bubble stack.
119      */
hidenull120     fun hide(fromExpansion: Boolean) {
121         if (visibility != VISIBLE || isHiding) return
122 
123         animate()
124             .alpha(0f)
125             .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
126             .withEndAction { visibility = GONE }
127     }
128 
setShouldShownull129     private fun setShouldShow(shouldShow: Boolean) {
130         context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
131                 .edit().putBoolean(PREF_STACK_EDUCATION, !shouldShow).apply()
132     }
133 }
134 
135 const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"