blob: a62edfd44ee8906cc0b12262e7a90b2172132447 [file] [log] [blame]
Chihhang Chuanga2181142018-06-05 15:29:06 +08001/*
2 * Copyright (C) 2018 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
17package com.android.phone;
18
Wesley.CW Wang5e785392018-08-09 20:11:34 +080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Chihhang Chuanga2181142018-06-05 15:29:06 +080021import android.annotation.Nullable;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ResolveInfo;
25import android.graphics.Bitmap;
26import android.graphics.drawable.Drawable;
27import android.os.UserHandle;
28import android.os.UserManager;
29import android.telephony.TelephonyManager;
Billy Chifef11c42018-06-15 19:00:15 +080030import android.text.TextUtils;
Chihhang Chuanga2181142018-06-05 15:29:06 +080031import android.util.AttributeSet;
Wesley.CW Wang5e785392018-08-09 20:11:34 +080032import android.view.MotionEvent;
Chihhang Chuanga2181142018-06-05 15:29:06 +080033import android.view.View;
Wesley.CW Wang5e785392018-08-09 20:11:34 +080034import android.view.ViewAnimationUtils;
35import android.view.accessibility.AccessibilityManager;
Chihhang Chuang0808a092018-07-02 11:08:50 +080036import android.widget.FrameLayout;
Chihhang Chuanga2181142018-06-05 15:29:06 +080037import android.widget.ImageView;
Chihhang Chuanga2181142018-06-05 15:29:06 +080038import android.widget.TextView;
39
40import androidx.core.graphics.drawable.RoundedBitmapDrawable;
41import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
42
43import com.android.internal.util.UserIcons;
44
45import java.util.List;
46
47/**
48 * EmergencyInfoGroup display user icon and user name. And it is an entry point to
49 * Emergency Information.
50 */
Wesley.CW Wang5e785392018-08-09 20:11:34 +080051public class EmergencyInfoGroup extends FrameLayout implements View.OnClickListener {
52 // Time to hide view of confirmation.
53 private static final long HIDE_DELAY_MS = 3000;
54 private static final int[] ICON_VIEWS =
55 {R.id.emergency_info_image, R.id.confirmed_emergency_info_image};
56
Chihhang Chuang0808a092018-07-02 11:08:50 +080057 private TextView mEmergencyInfoName;
Chihhang Chuanga2181142018-06-05 15:29:06 +080058 private View mEmergencyInfoButton;
Wesley.CW Wang5e785392018-08-09 20:11:34 +080059 private View mEmergencyInfoConfirmButton;
60
61 private MotionEvent mPendingTouchEvent;
62 private OnConfirmClickListener mOnConfirmClickListener;
63
64 private boolean mConfirmViewHiding;
Chihhang Chuanga2181142018-06-05 15:29:06 +080065
66 public EmergencyInfoGroup(Context context, @Nullable AttributeSet attrs) {
67 super(context, attrs);
68 }
69
Wesley.CW Wang5e785392018-08-09 20:11:34 +080070 /**
71 * Interface definition for a callback to be invoked when the view of confirmation on emergency
72 * info button is clicked.
73 */
74 public interface OnConfirmClickListener {
75 /**
76 * Called when the view of confirmation on emergency info button has been clicked.
77 *
78 * @param button The shortcut button that was clicked.
79 */
80 void onConfirmClick(EmergencyInfoGroup button);
81 }
82
83 /**
84 * Register a callback {@link OnConfirmClickListener} to be invoked when view of confirmation
85 * is clicked.
86 *
87 * @param onConfirmClickListener The callback that will run.
88 */
89 public void setOnConfirmClickListener(OnConfirmClickListener onConfirmClickListener) {
90 mOnConfirmClickListener = onConfirmClickListener;
91 }
92
Chihhang Chuanga2181142018-06-05 15:29:06 +080093 @Override
94 protected void onFinishInflate() {
95 super.onFinishInflate();
Wesley.CW Wang5e785392018-08-09 20:11:34 +080096 mEmergencyInfoButton = findViewById(R.id.emergency_info_view);
Chihhang Chuang0808a092018-07-02 11:08:50 +080097 mEmergencyInfoName = (TextView) findViewById(R.id.emergency_info_name);
Wesley.CW Wang5e785392018-08-09 20:11:34 +080098
99 mEmergencyInfoConfirmButton = findViewById(R.id.emergency_info_confirm_view);
100
101 mEmergencyInfoButton.setOnClickListener(this);
102 mEmergencyInfoConfirmButton.setOnClickListener(this);
103
104 mConfirmViewHiding = true;
Chihhang Chuanga2181142018-06-05 15:29:06 +0800105 }
106
107 @Override
108 protected void onWindowVisibilityChanged(int visibility) {
109 super.onWindowVisibilityChanged(visibility);
110 if (visibility == View.VISIBLE) {
111 setupButtonInfo();
112 }
113 }
114
115 private void setupButtonInfo() {
116 List<ResolveInfo> infos;
117
118 if (TelephonyManager.EMERGENCY_ASSISTANCE_ENABLED) {
119 infos = EmergencyAssistanceHelper.resolveAssistPackageAndQueryActivities(getContext());
120 } else {
121 infos = null;
122 }
123
124 boolean visible = false;
125
126 if (infos != null && infos.size() > 0) {
127 final String packageName = infos.get(0).activityInfo.packageName;
yuanjiahsubc20ba82018-11-26 22:57:18 +0800128 final Intent intent = new Intent(
yuanjiahsucc83a3b2019-05-23 15:29:30 +0800129 EmergencyAssistanceHelper.getIntentAction())
Chihhang Chuanga2181142018-06-05 15:29:06 +0800130 .setPackage(packageName);
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800131 setTag(R.id.tag_intent, intent);
132 setUserIcon();
Chihhang Chuang0808a092018-07-02 11:08:50 +0800133
Chihhang Chuanga2181142018-06-05 15:29:06 +0800134 visible = true;
135 }
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800136 mEmergencyInfoName.setText(getUserName());
Chihhang Chuanga2181142018-06-05 15:29:06 +0800137
138 setVisibility(visible ? View.VISIBLE : View.GONE);
139 }
140
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800141 private void setUserIcon() {
142 for (int iconView : ICON_VIEWS) {
143 ImageView userIcon = findViewById(iconView);
144 userIcon.setImageDrawable(getCircularUserIcon());
145 }
146 }
147
Chihhang Chuang0808a092018-07-02 11:08:50 +0800148 /**
149 * Get user icon.
150 *
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800151 * @return user icon, or default user icon if user do not set photo.
Chihhang Chuang0808a092018-07-02 11:08:50 +0800152 */
Chihhang Chuanga2181142018-06-05 15:29:06 +0800153 private Drawable getCircularUserIcon() {
154 final UserManager userManager = (UserManager) getContext().getSystemService(
155 Context.USER_SERVICE);
Meng Wanga4b29e12019-10-04 12:00:06 -0700156 Bitmap bitmapUserIcon = userManager.getUserIcon();
Chihhang Chuanga2181142018-06-05 15:29:06 +0800157
158 if (bitmapUserIcon == null) {
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800159 // get default user icon.
160 final Drawable defaultUserIcon = UserIcons.getDefaultUserIcon(
161 getContext().getResources(), UserHandle.myUserId(), false);
162 bitmapUserIcon = UserIcons.convertToBitmap(defaultUserIcon);
Chihhang Chuanga2181142018-06-05 15:29:06 +0800163 }
Chihhang Chuanga2181142018-06-05 15:29:06 +0800164 RoundedBitmapDrawable drawableUserIcon = RoundedBitmapDrawableFactory.create(
165 getContext().getResources(), bitmapUserIcon);
166 drawableUserIcon.setCircular(true);
167
168 return drawableUserIcon;
169 }
Billy Chifef11c42018-06-15 19:00:15 +0800170
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800171 private CharSequence getUserName() {
172 final UserManager userManager = (UserManager) getContext().getSystemService(
173 Context.USER_SERVICE);
174 final String userName = userManager.getUserName();
Chihhang Chuang0808a092018-07-02 11:08:50 +0800175
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800176 return TextUtils.isEmpty(userName) ? getContext().getText(
177 R.string.emergency_information_owner_hint) : userName;
Billy Chifef11c42018-06-15 19:00:15 +0800178 }
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800179
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800180 /**
181 * Called by the activity before a touch event is dispatched to the view hierarchy.
182 */
183 public void onPreTouchEvent(MotionEvent event) {
184 mPendingTouchEvent = event;
185 }
186
187 /**
188 * Called by the activity after a touch event is dispatched to the view hierarchy.
189 */
190 public void onPostTouchEvent(MotionEvent event) {
191 // Hide the confirmation button if a touch event was delivered to the activity but not to
192 // this view.
193 if (mPendingTouchEvent != null) {
194 hideSelectedButton();
195 }
196 mPendingTouchEvent = null;
197 }
198
199 @Override
200 public boolean dispatchTouchEvent(MotionEvent event) {
201 boolean handled = super.dispatchTouchEvent(event);
202 if (mPendingTouchEvent == event && handled) {
203 mPendingTouchEvent = null;
204 }
205 return handled;
206 }
207
208 @Override
209 public void onClick(View view) {
Amit Mahajan28a499c2019-11-20 15:13:42 -0800210 if (view.getId() == R.id.emergency_info_view) {
211 AccessibilityManager accessibilityMgr =
Amit Mahajanb8f13202020-01-27 18:16:07 -0800212 (AccessibilityManager) getContext().getSystemService(
Amit Mahajan28a499c2019-11-20 15:13:42 -0800213 Context.ACCESSIBILITY_SERVICE);
214 if (accessibilityMgr.isTouchExplorationEnabled()) {
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800215 if (mOnConfirmClickListener != null) {
216 mOnConfirmClickListener.onConfirmClick(this);
217 }
Amit Mahajan28a499c2019-11-20 15:13:42 -0800218 } else {
219 revealSelectedButton();
220 }
221 } else if (view.getId() == R.id.emergency_info_confirm_view) {
222 if (mOnConfirmClickListener != null) {
223 mOnConfirmClickListener.onConfirmClick(this);
224 }
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800225 }
226 }
227
228 private void revealSelectedButton() {
229 mConfirmViewHiding = false;
230
231 mEmergencyInfoConfirmButton.setVisibility(View.VISIBLE);
232 int centerX = mEmergencyInfoButton.getLeft() + mEmergencyInfoButton.getWidth() / 2;
233 int centerY = mEmergencyInfoButton.getTop() + mEmergencyInfoButton.getHeight() / 2;
234 Animator reveal = ViewAnimationUtils.createCircularReveal(
235 mEmergencyInfoConfirmButton,
236 centerX,
237 centerY,
238 0,
239 Math.max(centerX, mEmergencyInfoConfirmButton.getWidth() - centerX)
240 + Math.max(centerY, mEmergencyInfoConfirmButton.getHeight() - centerY));
241 reveal.start();
242
243 postDelayed(mCancelSelectedButtonRunnable, HIDE_DELAY_MS);
244 mEmergencyInfoConfirmButton.requestFocus();
245 }
246
247 private void hideSelectedButton() {
248 if (mConfirmViewHiding || mEmergencyInfoConfirmButton.getVisibility() != VISIBLE) {
249 return;
250 }
251
252 mConfirmViewHiding = true;
253
254 removeCallbacks(mCancelSelectedButtonRunnable);
255 int centerX =
256 mEmergencyInfoConfirmButton.getLeft() + mEmergencyInfoConfirmButton.getWidth() / 2;
257 int centerY =
258 mEmergencyInfoConfirmButton.getTop() + mEmergencyInfoConfirmButton.getHeight() / 2;
259 Animator reveal = ViewAnimationUtils.createCircularReveal(
260 mEmergencyInfoConfirmButton,
261 centerX,
262 centerY,
263 Math.max(centerX, mEmergencyInfoButton.getWidth() - centerX)
264 + Math.max(centerY, mEmergencyInfoButton.getHeight() - centerY),
265 0);
266 reveal.addListener(new AnimatorListenerAdapter() {
267 @Override
268 public void onAnimationEnd(Animator animation) {
269 mEmergencyInfoConfirmButton.setVisibility(INVISIBLE);
270 }
271 });
272 reveal.start();
273
274 mEmergencyInfoButton.requestFocus();
275 }
276
277 private final Runnable mCancelSelectedButtonRunnable = new Runnable() {
278 @Override
279 public void run() {
280 if (!isAttachedToWindow()) return;
281 hideSelectedButton();
282 }
283 };
CY Cheng62afa1c2018-08-24 18:33:10 +0800284}