Loading src/com/android/launcher3/Launcher.java +5 −8 Original line number Diff line number Diff line Loading @@ -183,7 +183,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.pm.UserCache; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; Loading @@ -208,7 +207,6 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; import com.android.launcher3.util.SystemUiController; Loading Loading @@ -411,8 +409,6 @@ public class Launcher extends StatefulActivity<LauncherState> protected long mLastTouchUpTime = -1; private boolean mTouchInProgress; private SafeCloseable mUserChangedCallbackCloseable; // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. // User actions within AllApps state are logged with this InstanceId, to recreate AllApps Loading Loading @@ -581,9 +577,6 @@ public class Launcher extends StatefulActivity<LauncherState> mRotationHelper.initialize(); TraceHelper.INSTANCE.endSection(); mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( () -> getStateManager().goToState(NORMAL)); if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); } Loading Loading @@ -1804,7 +1797,6 @@ public class Launcher extends StatefulActivity<LauncherState> LauncherAppState.getIDP(this).removeOnChangeListener(this); mOverlayManager.onActivityDestroyed(this); mUserChangedCallbackCloseable.close(); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { Loading Loading @@ -2974,9 +2966,14 @@ public class Launcher extends StatefulActivity<LauncherState> public void bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap) { Preconditions.assertUIThread(); boolean hadWorkApps = mAppsView.shouldShowTabs(); AllAppsStore appsStore = mAppsView.getAppsStore(); appsStore.setApps(apps, flags, packageUserKeytoUidMap); PopupContainerWithArrow.dismissInvalidPopup(this); if (hadWorkApps != mAppsView.shouldShowTabs()) { getStateManager().goToState(NORMAL); } if (Utilities.ATLEAST_S) { Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, DISPLAY_ALL_APPS_TRACE_COOKIE); Loading src/com/android/launcher3/LauncherAppState.java +1 −5 Original line number Diff line number Diff line Loading @@ -111,10 +111,6 @@ public class LauncherAppState implements SafeCloseable { SimpleBroadcastReceiver modelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNLOCKED, Intent.ACTION_PROFILE_INACCESSIBLE, ACTION_DEVICE_POLICY_RESOURCE_UPDATED); if (FeatureFlags.IS_STUDIO_BUILD) { modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD); Loading @@ -122,7 +118,7 @@ public class LauncherAppState implements SafeCloseable { mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) .addUserChangeListener(mModel::forceReload); .addUserEventListener(mModel::onUserEvent); mOnTerminateCallback.add(userChangeListener::close); LockedUserState.get(context).runOnUserUnlocked(() -> { Loading src/com/android/launcher3/LauncherModel.java +27 −31 Original line number Diff line number Diff line Loading @@ -95,15 +95,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi static final String TAG = "Launcher.Model"; // Broadcast intent to track when the profile gets locked: // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets // locked when paused. // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only // appeared in Android S. private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; @NonNull private final LauncherAppState mApp; @NonNull Loading Loading @@ -303,28 +294,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action) || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) { UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + " user: " + user); } if (user != null) { if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { enqueueModelUpdateTask(new PackageUpdatedTask( PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); } if (ACTION_PROFILE_LOCKED.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { enqueueModelUpdateTask(new UserLockStateChangedTask( user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action))); } } } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) { enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate)); } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { Loading @@ -336,6 +305,33 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } /** * Called then there use a user event * @see UserCache#addUserEventListener */ public void onUserEvent(UserHandle user, String action) { if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + " user: " + user); } if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { enqueueModelUpdateTask(new PackageUpdatedTask( PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); } if (UserCache.ACTION_PROFILE_LOCKED.equals(action) || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) { enqueueModelUpdateTask(new UserLockStateChangedTask( user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action))); } if (UserCache.ACTION_PROFILE_ADDED.equals(action) || UserCache.ACTION_PROFILE_REMOVED.equals(action)) { forceReload(); } } /** * Reloads the workspace items from the DB and re-binds the workspace. This should generally * not be called as DB updates are automatically followed by UI update Loading src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +4 −1 Original line number Diff line number Diff line Loading @@ -1150,7 +1150,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } } protected boolean shouldShowTabs() { /** * Returns true if the container has work apps. */ public boolean shouldShowTabs() { return mHasWorkApps; } Loading src/com/android/launcher3/pm/UserCache.java +80 −88 Original line number Diff line number Diff line Loading @@ -16,16 +16,21 @@ package com.android.launcher3.pm; import static com.android.launcher3.Utilities.ATLEAST_U; import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import android.content.Intent; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.LongSparseArray; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SafeCloseable; Loading @@ -34,136 +39,123 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; /** * Class which manages a local cache of user handles to avoid system rpc */ public class UserCache { public class UserCache implements SafeCloseable { public static final String ACTION_PROFILE_ADDED = ATLEAST_U ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; public static final String ACTION_PROFILE_REMOVED = ATLEAST_U ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; public static final String ACTION_PROFILE_LOCKED = ATLEAST_U ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; public static final MainThreadInitializedObject<UserCache> INSTANCE = new MainThreadInitializedObject<>(UserCache::new); private final Context mContext; private final UserManager mUserManager; private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>(); private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>(); private final SimpleBroadcastReceiver mUserChangeReceiver = new SimpleBroadcastReceiver(this::onUsersChanged); private LongSparseArray<UserHandle> mUsers; // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same // and not {@link Object#equals} private ArrayMap<UserHandle, Long> mUserToSerialMap; private final Context mContext; @NonNull private Map<UserHandle, Long> mUserToSerialMap; private UserCache(Context context) { mContext = context; mUserManager = context.getSystemService(UserManager.class); mUserToSerialMap = Collections.emptyMap(); MODEL_EXECUTOR.execute(this::initAsync); } private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); enableAndResetCache(); mUserChangeListeners.forEach(Runnable::run); @Override public void close() { MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); } /** * Adds a listener for user additions and removals */ public SafeCloseable addUserChangeListener(Runnable command) { synchronized (this) { if (mUserChangeListeners.isEmpty()) { // Enable caching and start listening for user broadcast @WorkerThread private void initAsync() { mUserChangeReceiver.register(mContext, Intent.ACTION_MANAGED_PROFILE_ADDED, Intent.ACTION_MANAGED_PROFILE_REMOVED); enableAndResetCache(); } mUserChangeListeners.add(command); return () -> removeUserChangeListener(command); } Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, ACTION_PROFILE_ADDED, ACTION_PROFILE_REMOVED, ACTION_PROFILE_UNLOCKED, ACTION_PROFILE_LOCKED); updateCache(); } private void enableAndResetCache() { synchronized (this) { mUsers = new LongSparseArray<>(); mUserToSerialMap = new ArrayMap<>(); List<UserHandle> users = mUserManager.getUserProfiles(); if (users != null) { for (UserHandle user : users) { testLogD(WORK_TAB_MISSING, "caching user: " + user); long serial = mUserManager.getSerialNumberForUser(user); mUsers.put(serial, user); mUserToSerialMap.put(user, serial); } } @AnyThread private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); MODEL_EXECUTOR.execute(this::updateCache); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); if (user == null) { return; } String action = intent.getAction(); mUserEventListeners.forEach(l -> l.accept(user, action)); } private void removeUserChangeListener(Runnable command) { synchronized (this) { mUserChangeListeners.remove(command); if (mUserChangeListeners.isEmpty()) { // Disable cache and stop listening mContext.unregisterReceiver(mUserChangeReceiver); mUsers = null; mUserToSerialMap = null; } @WorkerThread private void updateCache() { mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class)); } /** * Adds a listener for user additions and removals */ public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) { mUserEventListeners.add(listener); return () -> mUserEventListeners.remove(listener); } /** * @see UserManager#getSerialNumberForUser(UserHandle) */ public long getSerialNumberForUser(UserHandle user) { synchronized (this) { if (mUserToSerialMap != null) { Long serial = mUserToSerialMap.get(user); return serial == null ? 0 : serial; } } return mUserManager.getSerialNumberForUser(user); } /** * @see UserManager#getUserForSerialNumber(long) */ public UserHandle getUserForSerialNumber(long serialNumber) { synchronized (this) { if (mUsers != null) { return mUsers.get(serialNumber); } } return mUserManager.getUserForSerialNumber(serialNumber); Long value = serialNumber; return mUserToSerialMap .entrySet() .stream() .filter(entry -> value.equals(entry.getValue())) .findFirst() .map(Map.Entry::getKey) .orElse(Process.myUserHandle()); } /** * @see UserManager#getUserProfiles() */ public List<UserHandle> getUserProfiles() { StringBuilder usersToReturn = new StringBuilder(); List<UserHandle> users; if (sDebugTracing) { users = mUserManager.getUserProfiles(); for (UserHandle u : users) { usersToReturn.append(u).append(" && "); } testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn); return List.copyOf(mUserToSerialMap.keySet()); } synchronized (this) { if (mUsers != null) { usersToReturn = new StringBuilder(); for (UserHandle u : mUserToSerialMap.keySet()) { usersToReturn.append(u).append(" && "); } testLogD(WORK_TAB_MISSING, "users from cache: " + usersToReturn); return new ArrayList<>(mUserToSerialMap.keySet()); } else { testLogD(WORK_TAB_MISSING, "users from cache null"); private static Map<UserHandle, Long> queryAllUsers(UserManager userManager) { Map<UserHandle, Long> users = new ArrayMap<>(); List<UserHandle> usersActual = userManager.getUserProfiles(); if (usersActual != null) { for (UserHandle user : usersActual) { long serial = userManager.getSerialNumberForUser(user); users.put(user, serial); } } users = mUserManager.getUserProfiles(); return users == null ? Collections.emptyList() : users; return users; } } Loading
src/com/android/launcher3/Launcher.java +5 −8 Original line number Diff line number Diff line Loading @@ -183,7 +183,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pageindicators.WorkspacePageIndicator; import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.pm.UserCache; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; Loading @@ -208,7 +207,6 @@ import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; import com.android.launcher3.util.SystemUiController; Loading Loading @@ -411,8 +409,6 @@ public class Launcher extends StatefulActivity<LauncherState> protected long mLastTouchUpTime = -1; private boolean mTouchInProgress; private SafeCloseable mUserChangedCallbackCloseable; // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions. // When Launcher is not in AllApps state mAllAppsSessionLogId will be null. // User actions within AllApps state are logged with this InstanceId, to recreate AllApps Loading Loading @@ -581,9 +577,6 @@ public class Launcher extends StatefulActivity<LauncherState> mRotationHelper.initialize(); TraceHelper.INSTANCE.endSection(); mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener( () -> getStateManager().goToState(NORMAL)); if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); } Loading Loading @@ -1804,7 +1797,6 @@ public class Launcher extends StatefulActivity<LauncherState> LauncherAppState.getIDP(this).removeOnChangeListener(this); mOverlayManager.onActivityDestroyed(this); mUserChangedCallbackCloseable.close(); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { Loading Loading @@ -2974,9 +2966,14 @@ public class Launcher extends StatefulActivity<LauncherState> public void bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap) { Preconditions.assertUIThread(); boolean hadWorkApps = mAppsView.shouldShowTabs(); AllAppsStore appsStore = mAppsView.getAppsStore(); appsStore.setApps(apps, flags, packageUserKeytoUidMap); PopupContainerWithArrow.dismissInvalidPopup(this); if (hadWorkApps != mAppsView.shouldShowTabs()) { getStateManager().goToState(NORMAL); } if (Utilities.ATLEAST_S) { Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME, DISPLAY_ALL_APPS_TRACE_COOKIE); Loading
src/com/android/launcher3/LauncherAppState.java +1 −5 Original line number Diff line number Diff line Loading @@ -111,10 +111,6 @@ public class LauncherAppState implements SafeCloseable { SimpleBroadcastReceiver modelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNLOCKED, Intent.ACTION_PROFILE_INACCESSIBLE, ACTION_DEVICE_POLICY_RESOURCE_UPDATED); if (FeatureFlags.IS_STUDIO_BUILD) { modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD); Loading @@ -122,7 +118,7 @@ public class LauncherAppState implements SafeCloseable { mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) .addUserChangeListener(mModel::forceReload); .addUserEventListener(mModel::onUserEvent); mOnTerminateCallback.add(userChangeListener::close); LockedUserState.get(context).runOnUserUnlocked(() -> { Loading
src/com/android/launcher3/LauncherModel.java +27 −31 Original line number Diff line number Diff line Loading @@ -95,15 +95,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi static final String TAG = "Launcher.Model"; // Broadcast intent to track when the profile gets locked: // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets // locked when paused. // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only // appeared in Android S. private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; @NonNull private final LauncherAppState mApp; @NonNull Loading Loading @@ -303,28 +294,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action) || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) { UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + " user: " + user); } if (user != null) { if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { enqueueModelUpdateTask(new PackageUpdatedTask( PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); } if (ACTION_PROFILE_LOCKED.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { enqueueModelUpdateTask(new UserLockStateChangedTask( user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action))); } } } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) { enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate)); } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { Loading @@ -336,6 +305,33 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } /** * Called then there use a user event * @see UserCache#addUserEventListener */ public void onUserEvent(UserHandle user, String action) { if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action + " user: " + user); } if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { enqueueModelUpdateTask(new PackageUpdatedTask( PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); } if (UserCache.ACTION_PROFILE_LOCKED.equals(action) || UserCache.ACTION_PROFILE_UNLOCKED.equals(action)) { enqueueModelUpdateTask(new UserLockStateChangedTask( user, UserCache.ACTION_PROFILE_UNLOCKED.equals(action))); } if (UserCache.ACTION_PROFILE_ADDED.equals(action) || UserCache.ACTION_PROFILE_REMOVED.equals(action)) { forceReload(); } } /** * Reloads the workspace items from the DB and re-binds the workspace. This should generally * not be called as DB updates are automatically followed by UI update Loading
src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +4 −1 Original line number Diff line number Diff line Loading @@ -1150,7 +1150,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext> } } protected boolean shouldShowTabs() { /** * Returns true if the container has work apps. */ public boolean shouldShowTabs() { return mHasWorkApps; } Loading
src/com/android/launcher3/pm/UserCache.java +80 −88 Original line number Diff line number Diff line Loading @@ -16,16 +16,21 @@ package com.android.launcher3.pm; import static com.android.launcher3.Utilities.ATLEAST_U; import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING; import static com.android.launcher3.testing.shared.TestProtocol.sDebugTracing; import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import android.content.Intent; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.LongSparseArray; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SafeCloseable; Loading @@ -34,136 +39,123 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; /** * Class which manages a local cache of user handles to avoid system rpc */ public class UserCache { public class UserCache implements SafeCloseable { public static final String ACTION_PROFILE_ADDED = ATLEAST_U ? Intent.ACTION_PROFILE_ADDED : Intent.ACTION_MANAGED_PROFILE_ADDED; public static final String ACTION_PROFILE_REMOVED = ATLEAST_U ? Intent.ACTION_PROFILE_REMOVED : Intent.ACTION_MANAGED_PROFILE_REMOVED; public static final String ACTION_PROFILE_UNLOCKED = ATLEAST_U ? Intent.ACTION_PROFILE_ACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNLOCKED; public static final String ACTION_PROFILE_LOCKED = ATLEAST_U ? Intent.ACTION_PROFILE_INACCESSIBLE : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE; public static final MainThreadInitializedObject<UserCache> INSTANCE = new MainThreadInitializedObject<>(UserCache::new); private final Context mContext; private final UserManager mUserManager; private final ArrayList<Runnable> mUserChangeListeners = new ArrayList<>(); private final List<BiConsumer<UserHandle, String>> mUserEventListeners = new ArrayList<>(); private final SimpleBroadcastReceiver mUserChangeReceiver = new SimpleBroadcastReceiver(this::onUsersChanged); private LongSparseArray<UserHandle> mUsers; // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same // and not {@link Object#equals} private ArrayMap<UserHandle, Long> mUserToSerialMap; private final Context mContext; @NonNull private Map<UserHandle, Long> mUserToSerialMap; private UserCache(Context context) { mContext = context; mUserManager = context.getSystemService(UserManager.class); mUserToSerialMap = Collections.emptyMap(); MODEL_EXECUTOR.execute(this::initAsync); } private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); enableAndResetCache(); mUserChangeListeners.forEach(Runnable::run); @Override public void close() { MODEL_EXECUTOR.execute(() -> mUserChangeReceiver.unregisterReceiverSafely(mContext)); } /** * Adds a listener for user additions and removals */ public SafeCloseable addUserChangeListener(Runnable command) { synchronized (this) { if (mUserChangeListeners.isEmpty()) { // Enable caching and start listening for user broadcast @WorkerThread private void initAsync() { mUserChangeReceiver.register(mContext, Intent.ACTION_MANAGED_PROFILE_ADDED, Intent.ACTION_MANAGED_PROFILE_REMOVED); enableAndResetCache(); } mUserChangeListeners.add(command); return () -> removeUserChangeListener(command); } Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, ACTION_PROFILE_ADDED, ACTION_PROFILE_REMOVED, ACTION_PROFILE_UNLOCKED, ACTION_PROFILE_LOCKED); updateCache(); } private void enableAndResetCache() { synchronized (this) { mUsers = new LongSparseArray<>(); mUserToSerialMap = new ArrayMap<>(); List<UserHandle> users = mUserManager.getUserProfiles(); if (users != null) { for (UserHandle user : users) { testLogD(WORK_TAB_MISSING, "caching user: " + user); long serial = mUserManager.getSerialNumberForUser(user); mUsers.put(serial, user); mUserToSerialMap.put(user, serial); } } @AnyThread private void onUsersChanged(Intent intent) { testLogD(WORK_TAB_MISSING, "onUsersChanged intent: " + intent); MODEL_EXECUTOR.execute(this::updateCache); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); if (user == null) { return; } String action = intent.getAction(); mUserEventListeners.forEach(l -> l.accept(user, action)); } private void removeUserChangeListener(Runnable command) { synchronized (this) { mUserChangeListeners.remove(command); if (mUserChangeListeners.isEmpty()) { // Disable cache and stop listening mContext.unregisterReceiver(mUserChangeReceiver); mUsers = null; mUserToSerialMap = null; } @WorkerThread private void updateCache() { mUserToSerialMap = queryAllUsers(mContext.getSystemService(UserManager.class)); } /** * Adds a listener for user additions and removals */ public SafeCloseable addUserEventListener(BiConsumer<UserHandle, String> listener) { mUserEventListeners.add(listener); return () -> mUserEventListeners.remove(listener); } /** * @see UserManager#getSerialNumberForUser(UserHandle) */ public long getSerialNumberForUser(UserHandle user) { synchronized (this) { if (mUserToSerialMap != null) { Long serial = mUserToSerialMap.get(user); return serial == null ? 0 : serial; } } return mUserManager.getSerialNumberForUser(user); } /** * @see UserManager#getUserForSerialNumber(long) */ public UserHandle getUserForSerialNumber(long serialNumber) { synchronized (this) { if (mUsers != null) { return mUsers.get(serialNumber); } } return mUserManager.getUserForSerialNumber(serialNumber); Long value = serialNumber; return mUserToSerialMap .entrySet() .stream() .filter(entry -> value.equals(entry.getValue())) .findFirst() .map(Map.Entry::getKey) .orElse(Process.myUserHandle()); } /** * @see UserManager#getUserProfiles() */ public List<UserHandle> getUserProfiles() { StringBuilder usersToReturn = new StringBuilder(); List<UserHandle> users; if (sDebugTracing) { users = mUserManager.getUserProfiles(); for (UserHandle u : users) { usersToReturn.append(u).append(" && "); } testLogD(WORK_TAB_MISSING, "users from userManager: " + usersToReturn); return List.copyOf(mUserToSerialMap.keySet()); } synchronized (this) { if (mUsers != null) { usersToReturn = new StringBuilder(); for (UserHandle u : mUserToSerialMap.keySet()) { usersToReturn.append(u).append(" && "); } testLogD(WORK_TAB_MISSING, "users from cache: " + usersToReturn); return new ArrayList<>(mUserToSerialMap.keySet()); } else { testLogD(WORK_TAB_MISSING, "users from cache null"); private static Map<UserHandle, Long> queryAllUsers(UserManager userManager) { Map<UserHandle, Long> users = new ArrayMap<>(); List<UserHandle> usersActual = userManager.getUserProfiles(); if (usersActual != null) { for (UserHandle user : usersActual) { long serial = userManager.getSerialNumberForUser(user); users.put(user, serial); } } users = mUserManager.getUserProfiles(); return users == null ? Collections.emptyList() : users; return users; } }