content: /*
   * 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.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";
  
      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);
          View view = 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) view.findViewById(R.id.user_loading_avatar))
                      .setImageDrawable(drawable);
          }
  
          TextView msgView = view.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(view);
      }
  
      /**
       * 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.
          }
      }
  }
  