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