blob: 0e3480131952272b2634677f30d7fff85138ac0b [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowInsets;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
/**
* Dialog to show when a user switch it about to happen for the car. The intent is to snapshot the
* screen immediately after the dialog shows so that the user is informed that something is
* happening in the background rather than just freeze the screen and not know if the user-switch
* affordance was being handled.
*/
final class CarUserSwitchingDialog extends UserSwitchingDialog {
private static final String TAG = "ActivityManagerCarUserSwitchingDialog";
private View mView;
public CarUserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser,
UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage,
String switchingToSystemUserMessage) {
super(service, context, oldUser, newUser, aboveSystem, switchingFromSystemUserMessage,
switchingToSystemUserMessage);
}
@Override
void inflateContent() {
// Set up the dialog contents
setCancelable(false);
Resources res = getContext().getResources();
// Custom view due to alignment and font size requirements
getContext().setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert_UserSwitchingDialog);
mView = LayoutInflater.from(getContext()).inflate(
R.layout.car_user_switching_dialog,
null);
UserManager userManager =
(UserManager) getContext().getSystemService(Context.USER_SERVICE);
Bitmap bitmap = userManager.getUserIcon(mNewUser.id);
if (bitmap != null) {
CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(bitmap,
res.getDimension(R.dimen.car_fullscreen_user_pod_image_avatar_height));
((ImageView) mView.findViewById(R.id.user_loading_avatar))
.setImageDrawable(drawable);
}
TextView msgView = mView.findViewById(R.id.user_loading);
// TODO(b/145132885): use constant from CarSettings
boolean showInfo = "true".equals(Settings.Global.getString(
getContext().getContentResolver(),
"android.car.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE"));
if (showInfo) {
msgView.setText(res.getString(R.string.car_loading_profile) + " user\n(from "
+ mOldUser.id + " to " + mNewUser.id + ")");
} else {
msgView.setText(res.getString(R.string.car_loading_profile));
}
setView(mView);
}
@Override
public void show() {
super.show();
hideNavigationBar();
}
private void hideNavigationBar() {
mView.getWindowInsetsController().hide(WindowInsets.Type.navigationBars());
}
/**
* Converts the user icon to a circularly clipped one. This is used in the User Picker and
* Settings.
*/
static class CircleFramedDrawable extends Drawable {
private final Bitmap mBitmap;
private final int mSize;
private final Paint mPaint;
private float mScale;
private Rect mSrcRect;
private RectF mDstRect;
public static CircleFramedDrawable getInstance(Bitmap icon, float iconSize) {
CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
return instance;
}
public CircleFramedDrawable(Bitmap icon, int size) {
super();
mSize = size;
mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(mBitmap);
final int width = icon.getWidth();
final int height = icon.getHeight();
final int square = Math.min(width, height);
final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2,
square, square);
final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
final Path fillPath = new Path();
fillPath.addArc(circleRect, 0f, 360f);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
// opaque circle
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(fillPath, mPaint);
// mask in the icon where the bitmap is opaque
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
// prepare paint for frame drawing
mPaint.setXfermode(null);
mScale = 1f;
mSrcRect = new Rect(0, 0, mSize, mSize);
mDstRect = new RectF(0, 0, mSize, mSize);
}
@Override
public void draw(Canvas canvas) {
final float inside = mScale * mSize;
final float pad = (mSize - inside) / 2f;
mDstRect.set(pad, pad, mSize - pad, mSize - pad);
canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int alpha) {
// Needed to implement abstract method. Do nothing.
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
// Needed to implement abstract method. Do nothing.
}
}
}