1 /* 2 * Copyright (C) 2024 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.systemui.car.users; 18 19 import android.annotation.UserIdInt; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.UserInfo; 25 import android.os.UserManager; 26 import android.util.ArraySet; 27 28 import androidx.annotation.GuardedBy; 29 30 import com.android.systemui.CoreStartable; 31 import com.android.systemui.broadcast.BroadcastDispatcher; 32 import com.android.systemui.car.userswitcher.UserIconProvider; 33 import com.android.systemui.dagger.SysUISingleton; 34 import com.android.systemui.dagger.qualifiers.Main; 35 import com.android.systemui.settings.UserTracker; 36 37 import java.util.Set; 38 import java.util.concurrent.Executor; 39 40 import javax.inject.Inject; 41 42 /** 43 * CoreStartable service to keep the user icon updated and allow other components to listen to 44 * these updates. 45 */ 46 @SysUISingleton 47 public class CarProfileIconUpdater implements CoreStartable { 48 private final Context mContext; 49 private final Executor mMainExecutor; 50 private final UserTracker mUserTracker; 51 private final UserManager mUserManager; 52 private final BroadcastDispatcher mBroadcastDispatcher; 53 private final UserIconProvider mUserIconProvider; 54 @GuardedBy("mCallbacks") 55 private final Set<Callback> mCallbacks = new ArraySet<>(); 56 57 private boolean mUserLifecycleListenerRegistered; 58 private String mLastUserName; 59 60 private final UserTracker.Callback mUserChangedCallback = 61 new UserTracker.Callback() { 62 @Override 63 public void onUserChanged(int newUser, Context userContext) { 64 mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver); 65 registerForUserInfoChange(); 66 } 67 }; 68 69 private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() { 70 @Override 71 public void onReceive(Context context, Intent intent) { 72 updateUserIcon(mUserTracker.getUserId()); 73 } 74 }; 75 76 @Inject CarProfileIconUpdater(Context context, @Main Executor mainExecutor, UserTracker userTracker, UserManager userManager, BroadcastDispatcher broadcastDispatcher, UserIconProvider userIconProvider)77 public CarProfileIconUpdater(Context context, @Main Executor mainExecutor, 78 UserTracker userTracker, UserManager userManager, 79 BroadcastDispatcher broadcastDispatcher, UserIconProvider userIconProvider) { 80 mContext = context; 81 mMainExecutor = mainExecutor; 82 mUserTracker = userTracker; 83 mUserManager = userManager; 84 mBroadcastDispatcher = broadcastDispatcher; 85 mUserIconProvider = userIconProvider; 86 } 87 88 @Override start()89 public void start() { 90 registerForUserChangeEvents(); 91 } 92 93 /** Add a callback to listen to user icon updates */ addCallback(Callback callback)94 public void addCallback(Callback callback) { 95 synchronized (mCallbacks) { 96 mCallbacks.add(callback); 97 } 98 } 99 100 /** Remove callback for user icon updates */ removeCallback(Callback callback)101 public void removeCallback(Callback callback) { 102 synchronized (mCallbacks) { 103 mCallbacks.remove(callback); 104 } 105 } 106 updateUserIcon(@serIdInt int userId)107 protected void updateUserIcon(@UserIdInt int userId) { 108 UserInfo currentUserInfo = mUserManager.getUserInfo(userId); 109 110 // Update user icon with the first letter of the user name 111 if (mLastUserName == null || !mLastUserName.equals(currentUserInfo.name)) { 112 mLastUserName = currentUserInfo.name; 113 mUserIconProvider.setRoundedUserIcon(userId); 114 notifyCallbacks(userId); 115 } 116 } 117 notifyCallbacks(@serIdInt int userId)118 protected void notifyCallbacks(@UserIdInt int userId) { 119 synchronized (mCallbacks) { 120 for (Callback callback : mCallbacks) { 121 callback.onUserIconUpdated(userId); 122 } 123 } 124 } 125 registerForUserChangeEvents()126 private void registerForUserChangeEvents() { 127 if (mUserLifecycleListenerRegistered) { 128 return; 129 } 130 mUserLifecycleListenerRegistered = true; 131 mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); 132 registerForUserInfoChange(); 133 } 134 registerForUserInfoChange()135 private void registerForUserInfoChange() { 136 mLastUserName = mUserManager.getUserInfo(mUserTracker.getUserId()).name; 137 IntentFilter filter = new IntentFilter(); 138 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 139 mBroadcastDispatcher.registerReceiver(mUserUpdateReceiver, filter, /* executor= */ null, 140 mUserTracker.getUserHandle()); 141 } 142 143 public interface Callback { 144 /** Called when the user icon is updated for a specific userId. */ onUserIconUpdated(int userId)145 void onUserIconUpdated(int userId); 146 } 147 } 148