blob: 14c24d8607c9f7b567f8d05d38d9ce117120c539 [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;
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +080038import android.widget.LinearLayout;
Chihhang Chuanga2181142018-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 Chuang0808a092018-07-02 11:08:50 +080058 private TextView mEmergencyInfoName;
Chihhang Chuanga2181142018-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 Chuanga2181142018-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 Chuanga2181142018-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 Chuang0808a092018-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 Chuanga2181142018-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;
yuanjiahsubc20ba82018-11-26 22:57:18 +0800129 final Intent intent = new Intent(
yuanjiahsucc83a3b2019-05-23 15:29:30 +0800130 EmergencyAssistanceHelper.getIntentAction())
Chihhang Chuanga2181142018-06-05 15:29:06 +0800131 .setPackage(packageName);
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800132 setTag(R.id.tag_intent, intent);
133 setUserIcon();
Chihhang Chuang0808a092018-07-02 11:08:50 +0800134
Chihhang Chuanga2181142018-06-05 15:29:06 +0800135 visible = true;
136 }
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800137 mEmergencyInfoName.setText(getUserName());
Chihhang Chuanga2181142018-06-05 15:29:06 +0800138
139 setVisibility(visible ? View.VISIBLE : View.GONE);
140 }
141
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800142 private void setUserIcon() {
143 for (int iconView : ICON_VIEWS) {
144 ImageView userIcon = findViewById(iconView);
145 userIcon.setImageDrawable(getCircularUserIcon());
146 }
147 }
148
Chihhang Chuang0808a092018-07-02 11:08:50 +0800149 /**
150 * Get user icon.
151 *
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800152 * @return user icon, or default user icon if user do not set photo.
Chihhang Chuang0808a092018-07-02 11:08:50 +0800153 */
Chihhang Chuanga2181142018-06-05 15:29:06 +0800154 private Drawable getCircularUserIcon() {
155 final UserManager userManager = (UserManager) getContext().getSystemService(
156 Context.USER_SERVICE);
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800157 Bitmap bitmapUserIcon = userManager.getUserIcon(UserHandle.getCallingUserId());
Chihhang Chuanga2181142018-06-05 15:29:06 +0800158
159 if (bitmapUserIcon == null) {
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800160 // get default user icon.
161 final Drawable defaultUserIcon = UserIcons.getDefaultUserIcon(
162 getContext().getResources(), UserHandle.myUserId(), false);
163 bitmapUserIcon = UserIcons.convertToBitmap(defaultUserIcon);
Chihhang Chuanga2181142018-06-05 15:29:06 +0800164 }
Chihhang Chuanga2181142018-06-05 15:29:06 +0800165 RoundedBitmapDrawable drawableUserIcon = RoundedBitmapDrawableFactory.create(
166 getContext().getResources(), bitmapUserIcon);
167 drawableUserIcon.setCircular(true);
168
169 return drawableUserIcon;
170 }
Billy Chifef11c42018-06-15 19:00:15 +0800171
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800172 private CharSequence getUserName() {
173 final UserManager userManager = (UserManager) getContext().getSystemService(
174 Context.USER_SERVICE);
175 final String userName = userManager.getUserName();
Chihhang Chuang0808a092018-07-02 11:08:50 +0800176
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800177 return TextUtils.isEmpty(userName) ? getContext().getText(
178 R.string.emergency_information_owner_hint) : userName;
Billy Chifef11c42018-06-15 19:00:15 +0800179 }
Wesley.CW Wanga7c20a72018-07-24 12:20:20 +0800180
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800181 /**
182 * Called by the activity before a touch event is dispatched to the view hierarchy.
183 */
184 public void onPreTouchEvent(MotionEvent event) {
185 mPendingTouchEvent = event;
186 }
187
188 /**
189 * Called by the activity after a touch event is dispatched to the view hierarchy.
190 */
191 public void onPostTouchEvent(MotionEvent event) {
192 // Hide the confirmation button if a touch event was delivered to the activity but not to
193 // this view.
194 if (mPendingTouchEvent != null) {
195 hideSelectedButton();
196 }
197 mPendingTouchEvent = null;
198 }
199
200 @Override
201 public boolean dispatchTouchEvent(MotionEvent event) {
202 boolean handled = super.dispatchTouchEvent(event);
203 if (mPendingTouchEvent == event && handled) {
204 mPendingTouchEvent = null;
205 }
206 return handled;
207 }
208
209 @Override
210 public void onClick(View view) {
211 switch (view.getId()) {
212 case R.id.emergency_info_view:
213 if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
214 if (mOnConfirmClickListener != null) {
215 mOnConfirmClickListener.onConfirmClick(this);
216 }
217 } else {
218 revealSelectedButton();
219 }
220 break;
221 case R.id.emergency_info_confirm_view:
222 if (mOnConfirmClickListener != null) {
223 mOnConfirmClickListener.onConfirmClick(this);
224 }
225 break;
226 default:
227 break;
228 }
229 }
230
231 private void revealSelectedButton() {
232 mConfirmViewHiding = false;
233
234 mEmergencyInfoConfirmButton.setVisibility(View.VISIBLE);
235 int centerX = mEmergencyInfoButton.getLeft() + mEmergencyInfoButton.getWidth() / 2;
236 int centerY = mEmergencyInfoButton.getTop() + mEmergencyInfoButton.getHeight() / 2;
237 Animator reveal = ViewAnimationUtils.createCircularReveal(
238 mEmergencyInfoConfirmButton,
239 centerX,
240 centerY,
241 0,
242 Math.max(centerX, mEmergencyInfoConfirmButton.getWidth() - centerX)
243 + Math.max(centerY, mEmergencyInfoConfirmButton.getHeight() - centerY));
244 reveal.start();
245
246 postDelayed(mCancelSelectedButtonRunnable, HIDE_DELAY_MS);
247 mEmergencyInfoConfirmButton.requestFocus();
248 }
249
250 private void hideSelectedButton() {
251 if (mConfirmViewHiding || mEmergencyInfoConfirmButton.getVisibility() != VISIBLE) {
252 return;
253 }
254
255 mConfirmViewHiding = true;
256
257 removeCallbacks(mCancelSelectedButtonRunnable);
258 int centerX =
259 mEmergencyInfoConfirmButton.getLeft() + mEmergencyInfoConfirmButton.getWidth() / 2;
260 int centerY =
261 mEmergencyInfoConfirmButton.getTop() + mEmergencyInfoConfirmButton.getHeight() / 2;
262 Animator reveal = ViewAnimationUtils.createCircularReveal(
263 mEmergencyInfoConfirmButton,
264 centerX,
265 centerY,
266 Math.max(centerX, mEmergencyInfoButton.getWidth() - centerX)
267 + Math.max(centerY, mEmergencyInfoButton.getHeight() - centerY),
268 0);
269 reveal.addListener(new AnimatorListenerAdapter() {
270 @Override
271 public void onAnimationEnd(Animator animation) {
272 mEmergencyInfoConfirmButton.setVisibility(INVISIBLE);
273 }
274 });
275 reveal.start();
276
277 mEmergencyInfoButton.requestFocus();
278 }
279
280 private final Runnable mCancelSelectedButtonRunnable = new Runnable() {
281 @Override
282 public void run() {
283 if (!isAttachedToWindow()) return;
284 hideSelectedButton();
285 }
286 };
287
288 /**
289 * Update layout margin when emergency shortcut button more than 2.
290 */
291 public void updateLayoutMargin() {
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +0800292 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
Wesley.CW Wang5e785392018-08-09 20:11:34 +0800293
294 params.topMargin = getResources().getDimensionPixelSize(
295 R.dimen.emergency_info_button_fix_margin_vertical);
296 params.bottomMargin = getResources().getDimensionPixelSize(
297 R.dimen.emergency_info_button_fix_margin_vertical);
298
Wesley.CW Wang7fce0e82018-07-17 18:05:09 +0800299 setLayoutParams(params);
300 }
CY Cheng62afa1c2018-08-24 18:33:10 +0800301}