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 17package com.android.server.am; 18 19import android.content.Context; 20import android.content.pm.UserInfo; 21import android.content.res.Resources; 22import android.graphics.Bitmap; 23import android.graphics.Canvas; 24import android.graphics.Color; 25import android.graphics.ColorFilter; 26import android.graphics.Paint; 27import android.graphics.Path; 28import android.graphics.PixelFormat; 29import android.graphics.PorterDuff; 30import android.graphics.PorterDuffXfermode; 31import android.graphics.Rect; 32import android.graphics.RectF; 33import android.graphics.drawable.ColorDrawable; 34import android.graphics.drawable.Drawable; 35import android.os.UserManager; 36import android.view.LayoutInflater; 37import android.view.View; 38import android.widget.ImageView; 39import android.widget.TextView; 40import com.android.internal.R; 41 42 43/** 44 * Dialog to show when a user switch it about to happen for the car. The intent is to snapshot the 45 * screen immediately after the dialog shows so that the user is informed that something is 46 * happening in the background rather than just freeze the screen and not know if the user-switch 47 * affordance was being handled. 48 */ 49final class CarUserSwitchingDialog extends UserSwitchingDialog { 50 51 private static final String TAG = "ActivityManagerCarUserSwitchingDialog"; 52 53 public CarUserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser, 54 UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage, 55 String switchingToSystemUserMessage) { 56 super(service, context, oldUser, newUser, aboveSystem, switchingFromSystemUserMessage, 57 switchingToSystemUserMessage); 58 59 getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 60 } 61 62 @Override 63 void inflateContent() { 64 // Set up the dialog contents 65 setCancelable(false); 66 Resources res = getContext().getResources(); 67 // Custom view due to alignment and font size requirements 68 View view = LayoutInflater.from(getContext()).inflate(R.layout.car_user_switching_dialog, 69 null); 70 71 UserManager userManager = 72 (UserManager) getContext().getSystemService(Context.USER_SERVICE); 73 Bitmap bitmap = userManager.getUserIcon(mNewUser.id); 74 if (bitmap != null) { 75 CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(bitmap, 76 res.getDimension(R.dimen.car_fullscreen_user_pod_image_avatar_height)); 77 ((ImageView) view.findViewById(R.id.user_loading_avatar)) 78 .setImageDrawable(drawable); 79 } 80 81 ((TextView) view.findViewById(R.id.user_loading)) 82 .setText(res.getString(R.string.car_loading_profile)); 83 setView(view); 84 } 85 86 /** 87 * Converts the user icon to a circularly clipped one. This is used in the User Picker and 88 * Settings. 89 */ 90 static class CircleFramedDrawable extends Drawable { 91 92 private final Bitmap mBitmap; 93 private final int mSize; 94 private final Paint mPaint; 95 96 private float mScale; 97 private Rect mSrcRect; 98 private RectF mDstRect; 99 100 public static CircleFramedDrawable getInstance(Bitmap icon, float iconSize) { 101 CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize); 102 return instance; 103 } 104 105 public CircleFramedDrawable(Bitmap icon, int size) { 106 super(); 107 mSize = size; 108 109 mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888); 110 final Canvas canvas = new Canvas(mBitmap); 111 112 final int width = icon.getWidth(); 113 final int height = icon.getHeight(); 114 final int square = Math.min(width, height); 115 116 final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, 117 square, square); 118 final RectF circleRect = new RectF(0f, 0f, mSize, mSize); 119 120 final Path fillPath = new Path(); 121 fillPath.addArc(circleRect, 0f, 360f); 122 123 canvas.drawColor(0, PorterDuff.Mode.CLEAR); 124 125 // opaque circle 126 mPaint = new Paint(); 127 mPaint.setAntiAlias(true); 128 mPaint.setColor(Color.BLACK); 129 mPaint.setStyle(Paint.Style.FILL); 130 canvas.drawPath(fillPath, mPaint); 131 132 // mask in the icon where the bitmap is opaque 133 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 134 canvas.drawBitmap(icon, cropRect, circleRect, mPaint); 135 136 // prepare paint for frame drawing 137 mPaint.setXfermode(null); 138 139 mScale = 1f; 140 141 mSrcRect = new Rect(0, 0, mSize, mSize); 142 mDstRect = new RectF(0, 0, mSize, mSize); 143 } 144 145 @Override 146 public void draw(Canvas canvas) { 147 final float inside = mScale * mSize; 148 final float pad = (mSize - inside) / 2f; 149 150 mDstRect.set(pad, pad, mSize - pad, mSize - pad); 151 canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); 152 } 153 154 @Override 155 public int getOpacity() { 156 return PixelFormat.TRANSLUCENT; 157 } 158 159 @Override 160 public void setAlpha(int alpha) { 161 // Needed to implement abstract method. Do nothing. 162 } 163 164 @Override 165 public void setColorFilter(ColorFilter colorFilter) { 166 // Needed to implement abstract method. Do nothing. 167 } 168 } 169} 170