blob: 5c0183438b2cb48e7965e04d848ba3bf0b59c2ff [file] [log] [blame]
Chihhang Chuangf264cfb2018-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 Chuangf264cfb2018-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 Chi17ec2282018-06-15 19:00:15 +080030import android.text.TextUtils;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080031import android.util.AttributeSet;
Wesley.CW Wang5e785392018-08-09 20:11:34 +080032import android.view.MotionEvent;
Chihhang Chuangf264cfb2018-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 Chuangf8d33002018-07-02 11:08:50 +080036import android.widget.FrameLayout;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080037import android.widget.ImageView;
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +080038import android.widget.LinearLayout;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080039import android.widget.TextView;
40
41import androidx.core.graphics.drawable.RoundedBitmapDrawable;
42import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
43
44import com.android.internal.util.UserIcons;
45
46import java.util.List;
47
48/**
49 * EmergencyInfoGroup display user icon and user name. And it is an entry point to
50 * Emergency Information.
51 */
Wesley.CW Wang5e785392018-08-09 20:11:34 +080052public class EmergencyInfoGroup extends FrameLayout implements View.OnClickListener {
53 // Time to hide view of confirmation.
54 private static final long HIDE_DELAY_MS = 3000;
55 private static final int[] ICON_VIEWS =
56 {R.id.emergency_info_image, R.id.confirmed_emergency_info_image};
57
Chihhang Chuangf8d33002018-07-02 11:08:50 +080058 private TextView mEmergencyInfoName;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080059 private View mEmergencyInfoButton;
Wesley.CW Wang5e785392018-08-09 20:11:34 +080060 private View mEmergencyInfoConfirmButton;
61
62 private MotionEvent mPendingTouchEvent;
63 private OnConfirmClickListener mOnConfirmClickListener;
64
65 private boolean mConfirmViewHiding;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080066
67 public EmergencyInfoGroup(Context context, @Nullable AttributeSet attrs) {
68 super(context, attrs);
69 }
70
Wesley.CW Wang5e785392018-08-09 20:11:34 +080071 /**
72 * Interface definition for a callback to be invoked when the view of confirmation on emergency
73 * info button is clicked.
74 */
75 public interface OnConfirmClickListener {
76 /**
77 * Called when the view of confirmation on emergency info button has been clicked.
78 *
79 * @param button The shortcut button that was clicked.
80 */
81 void onConfirmClick(EmergencyInfoGroup button);
82 }
83
84 /**
85 * Register a callback {@link OnConfirmClickListener} to be invoked when view of confirmation
86 * is clicked.
87 *
88 * @param onConfirmClickListener The callback that will run.
89 */
90 public void setOnConfirmClickListener(OnConfirmClickListener onConfirmClickListener) {
91 mOnConfirmClickListener = onConfirmClickListener;
92 }
93
Chihhang Chuangf264cfb2018-06-05 15:29:06 +080094 @Override
95 protected void onFinishInflate() {
96 super.onFinishInflate();
Wesley.CW Wang5e785392018-08-09 20:11:34 +080097 mEmergencyInfoButton = findViewById(R.id.emergency_info_view);
Chihhang Chuangf8d33002018-07-02 11:08:50 +080098 mEmergencyInfoName = (TextView) findViewById(R.id.emergency_info_name);
Wesley.CW Wang5e785392018-08-09 20:11:34 +080099
100 mEmergencyInfoConfirmButton = findViewById(R.id.emergency_info_confirm_view);
101
102 mEmergencyInfoButton.setOnClickListener(this);
103 mEmergencyInfoConfirmButton.setOnClickListener(this);
104
105 mConfirmViewHiding = true;
Chihhang Chuangf264cfb2018-06-05 15:29:06 +0800106 }
107
108 @Override
109 protected void onWindowVisibilityChanged(int visibility) {
110 super.onWindowVisibilityChanged(visibility);
111 if (visibility == View.VISIBLE) {
112 setupButtonInfo();
113 }
114 }
115
116 private void setupButtonInfo() {
117 List<ResolveInfo> infos;
118
119 if (TelephonyManager.EMERGENCY_ASSISTANCE_ENABLED) {
120 infos = EmergencyAssistanceHelper.resolveAssistPackageAndQueryActivities(getContext());
121 } else {
122 infos = null;
123 }
124
125 boolean visible = false;
126
127 if (infos != null && infos.size() > 0) {
128 final String packageName = infos.get(0).activityInfo.packageName;
129 final Intent intent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE)
130 .setPackage(packageName);
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800131 setTag(R.id.tag_intent, intent);
132 setUserIcon();
Chihhang Chuangf8d33002018-07-02 11:08:50 +0800133
Chihhang Chuangf264cfb2018-06-05 15:29:06 +0800134 visible = true;
135 }
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800136 mEmergencyInfoName.setText(getUserName());
Chihhang Chuangf264cfb2018-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 Chuangf8d33002018-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 Chuangf8d33002018-07-02 11:08:50 +0800152 */
Chihhang Chuangf264cfb2018-06-05 15:29:06 +0800153 private Drawable getCircularUserIcon() {
154 final UserManager userManager = (UserManager) getContext().getSystemService(
155 Context.USER_SERVICE);
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800156 Bitmap bitmapUserIcon = userManager.getUserIcon(UserHandle.getCallingUserId());
Chihhang Chuangf264cfb2018-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 Chuangf264cfb2018-06-05 15:29:06 +0800163 }
Chihhang Chuangf264cfb2018-06-05 15:29:06 +0800164 RoundedBitmapDrawable drawableUserIcon = RoundedBitmapDrawableFactory.create(
165 getContext().getResources(), bitmapUserIcon);
166 drawableUserIcon.setCircular(true);
167
168 return drawableUserIcon;
169 }
Billy Chi17ec2282018-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();
175
176 return TextUtils.isEmpty(userName) ? getContext().getText(
177 R.string.emergency_information_owner_hint) : userName;
178 }
179
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) {
210 switch (view.getId()) {
211 case R.id.emergency_info_view:
212 if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
213 if (mOnConfirmClickListener != null) {
214 mOnConfirmClickListener.onConfirmClick(this);
215 }
216 } else {
217 revealSelectedButton();
218 }
219 break;
220 case R.id.emergency_info_confirm_view:
221 if (mOnConfirmClickListener != null) {
222 mOnConfirmClickListener.onConfirmClick(this);
223 }
224 break;
225 default:
226 break;
227 }
228 }
229
230 private void revealSelectedButton() {
231 mConfirmViewHiding = false;
232
233 mEmergencyInfoConfirmButton.setVisibility(View.VISIBLE);
234 int centerX = mEmergencyInfoButton.getLeft() + mEmergencyInfoButton.getWidth() / 2;
235 int centerY = mEmergencyInfoButton.getTop() + mEmergencyInfoButton.getHeight() / 2;
236 Animator reveal = ViewAnimationUtils.createCircularReveal(
237 mEmergencyInfoConfirmButton,
238 centerX,
239 centerY,
240 0,
241 Math.max(centerX, mEmergencyInfoConfirmButton.getWidth() - centerX)
242 + Math.max(centerY, mEmergencyInfoConfirmButton.getHeight() - centerY));
243 reveal.start();
244
245 postDelayed(mCancelSelectedButtonRunnable, HIDE_DELAY_MS);
246 mEmergencyInfoConfirmButton.requestFocus();
247 }
248
249 private void hideSelectedButton() {
250 if (mConfirmViewHiding || mEmergencyInfoConfirmButton.getVisibility() != VISIBLE) {
251 return;
252 }
253
254 mConfirmViewHiding = true;
255
256 removeCallbacks(mCancelSelectedButtonRunnable);
257 int centerX =
258 mEmergencyInfoConfirmButton.getLeft() + mEmergencyInfoConfirmButton.getWidth() / 2;
259 int centerY =
260 mEmergencyInfoConfirmButton.getTop() + mEmergencyInfoConfirmButton.getHeight() / 2;
261 Animator reveal = ViewAnimationUtils.createCircularReveal(
262 mEmergencyInfoConfirmButton,
263 centerX,
264 centerY,
265 Math.max(centerX, mEmergencyInfoButton.getWidth() - centerX)
266 + Math.max(centerY, mEmergencyInfoButton.getHeight() - centerY),
267 0);
268 reveal.addListener(new AnimatorListenerAdapter() {
269 @Override
270 public void onAnimationEnd(Animator animation) {
271 mEmergencyInfoConfirmButton.setVisibility(INVISIBLE);
272 }
273 });
274 reveal.start();
275
276 mEmergencyInfoButton.requestFocus();
277 }
278
279 private final Runnable mCancelSelectedButtonRunnable = new Runnable() {
280 @Override
281 public void run() {
282 if (!isAttachedToWindow()) return;
283 hideSelectedButton();
284 }
285 };
286
287 /**
288 * Update layout margin when emergency shortcut button more than 2.
289 */
290 public void updateLayoutMargin() {
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +0800291 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800292
293 params.topMargin = getResources().getDimensionPixelSize(
294 R.dimen.emergency_info_button_fix_margin_vertical);
295 params.bottomMargin = getResources().getDimensionPixelSize(
296 R.dimen.emergency_info_button_fix_margin_vertical);
297
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +0800298 setLayoutParams(params);
299 }
Billy Chi17ec2282018-06-15 19:00:15 +0800300}