• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.phone;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ResolveInfo;
25 import android.graphics.Bitmap;
26 import android.graphics.drawable.Drawable;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.telephony.TelephonyManager;
30 import android.text.TextUtils;
31 import android.util.AttributeSet;
32 import android.view.MotionEvent;
33 import android.view.View;
34 import android.view.ViewAnimationUtils;
35 import android.view.accessibility.AccessibilityManager;
36 import android.widget.FrameLayout;
37 import android.widget.ImageView;
38 import android.widget.LinearLayout;
39 import android.widget.TextView;
40 
41 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
42 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
43 
44 import com.android.internal.util.UserIcons;
45 
46 import java.util.List;
47 
48 /**
49  * EmergencyInfoGroup display user icon and user name. And it is an entry point to
50  * Emergency Information.
51  */
52 public 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 
58     private TextView mEmergencyInfoName;
59     private View mEmergencyInfoButton;
60     private View mEmergencyInfoConfirmButton;
61 
62     private MotionEvent mPendingTouchEvent;
63     private OnConfirmClickListener mOnConfirmClickListener;
64 
65     private boolean mConfirmViewHiding;
66 
EmergencyInfoGroup(Context context, @Nullable AttributeSet attrs)67     public EmergencyInfoGroup(Context context, @Nullable AttributeSet attrs) {
68         super(context, attrs);
69     }
70 
71     /**
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          */
onConfirmClick(EmergencyInfoGroup button)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      */
setOnConfirmClickListener(OnConfirmClickListener onConfirmClickListener)90     public void setOnConfirmClickListener(OnConfirmClickListener onConfirmClickListener) {
91         mOnConfirmClickListener = onConfirmClickListener;
92     }
93 
94     @Override
onFinishInflate()95     protected void onFinishInflate() {
96         super.onFinishInflate();
97         mEmergencyInfoButton = findViewById(R.id.emergency_info_view);
98         mEmergencyInfoName = (TextView) findViewById(R.id.emergency_info_name);
99 
100         mEmergencyInfoConfirmButton = findViewById(R.id.emergency_info_confirm_view);
101 
102         mEmergencyInfoButton.setOnClickListener(this);
103         mEmergencyInfoConfirmButton.setOnClickListener(this);
104 
105         mConfirmViewHiding = true;
106     }
107 
108     @Override
onWindowVisibilityChanged(int visibility)109     protected void onWindowVisibilityChanged(int visibility) {
110         super.onWindowVisibilityChanged(visibility);
111         if (visibility == View.VISIBLE) {
112             setupButtonInfo();
113         }
114     }
115 
setupButtonInfo()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(
130                     EmergencyAssistanceHelper.getIntentAction())
131                     .setPackage(packageName);
132             setTag(R.id.tag_intent, intent);
133             setUserIcon();
134 
135             visible = true;
136         }
137         mEmergencyInfoName.setText(getUserName());
138 
139         setVisibility(visible ? View.VISIBLE : View.GONE);
140     }
141 
setUserIcon()142     private void setUserIcon() {
143         for (int iconView : ICON_VIEWS) {
144             ImageView userIcon = findViewById(iconView);
145             userIcon.setImageDrawable(getCircularUserIcon());
146         }
147     }
148 
149     /**
150      * Get user icon.
151      *
152      * @return user icon, or default user icon if user do not set photo.
153      */
getCircularUserIcon()154     private Drawable getCircularUserIcon() {
155         final UserManager userManager = (UserManager) getContext().getSystemService(
156                 Context.USER_SERVICE);
157         Bitmap bitmapUserIcon = userManager.getUserIcon(UserHandle.getCallingUserId());
158 
159         if (bitmapUserIcon == null) {
160             // get default user icon.
161             final Drawable defaultUserIcon = UserIcons.getDefaultUserIcon(
162                     getContext().getResources(), UserHandle.myUserId(), false);
163             bitmapUserIcon = UserIcons.convertToBitmap(defaultUserIcon);
164         }
165         RoundedBitmapDrawable drawableUserIcon = RoundedBitmapDrawableFactory.create(
166                 getContext().getResources(), bitmapUserIcon);
167         drawableUserIcon.setCircular(true);
168 
169         return drawableUserIcon;
170     }
171 
getUserName()172     private CharSequence getUserName() {
173         final UserManager userManager = (UserManager) getContext().getSystemService(
174                 Context.USER_SERVICE);
175         final String userName = userManager.getUserName();
176 
177         return TextUtils.isEmpty(userName) ? getContext().getText(
178                 R.string.emergency_information_owner_hint) : userName;
179     }
180 
181     /**
182      * Called by the activity before a touch event is dispatched to the view hierarchy.
183      */
onPreTouchEvent(MotionEvent event)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      */
onPostTouchEvent(MotionEvent event)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
dispatchTouchEvent(MotionEvent event)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
onClick(View view)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 
revealSelectedButton()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 
hideSelectedButton()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      */
updateLayoutMargin()291     public void updateLayoutMargin() {
292         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
293 
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 
299         setLayoutParams(params);
300     }
301 }
302