Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2015 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.launcher3.util;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.content.pm.LauncherActivityInfo;
     22 import android.os.Handler;
     23 import android.os.Process;
     24 import android.os.UserHandle;
     25 
     26 import com.android.launcher3.FolderInfo;
     27 import com.android.launcher3.InstallShortcutReceiver;
     28 import com.android.launcher3.ItemInfo;
     29 import com.android.launcher3.LauncherFiles;
     30 import com.android.launcher3.LauncherModel;
     31 import com.android.launcher3.MainThreadExecutor;
     32 import com.android.launcher3.R;
     33 import com.android.launcher3.SessionCommitReceiver;
     34 import com.android.launcher3.ShortcutInfo;
     35 import com.android.launcher3.Utilities;
     36 import com.android.launcher3.compat.UserManagerCompat;
     37 import com.android.launcher3.model.BgDataModel;
     38 import com.android.launcher3.model.ModelWriter;
     39 
     40 import java.util.ArrayList;
     41 import java.util.HashSet;
     42 import java.util.List;
     43 
     44 /**
     45  * Handles addition of app shortcuts for managed profiles.
     46  * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
     47  */
     48 public class ManagedProfileHeuristic {
     49 
     50     private static final String USER_FOLDER_ID_PREFIX = "user_folder_";
     51 
     52     /**
     53      * Duration (in milliseconds) for which app shortcuts will be added to work folder.
     54      */
     55     private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
     56 
     57     public static void onAllAppsLoaded(final Context context,
     58             List<LauncherActivityInfo> apps, UserHandle user) {
     59         if (Process.myUserHandle().equals(user)) {
     60             return;
     61         }
     62 
     63         UserFolderInfo ufi = new UserFolderInfo(context, user, null);
     64         // We only handle folder creation once. Later icon additions are handled using package
     65         // or session events.
     66         if (ufi.folderAlreadyCreated) {
     67             return;
     68         }
     69 
     70         if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) {
     71             // Just mark the folder id preference to avoid new folder creation later.
     72             ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
     73             return;
     74         }
     75 
     76         InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_BULK_ADD);
     77         for (LauncherActivityInfo app : apps) {
     78             // Queue all items which should go in the work folder.
     79             if (app.getFirstInstallTime() < ufi.addIconToFolderTime) {
     80                 InstallShortcutReceiver.queueActivityInfo(app, context);
     81             }
     82         }
     83         // Post the queue update on next frame, so that the loader gets finished.
     84         new Handler(LauncherModel.getWorkerLooper()).post(new Runnable() {
     85             @Override
     86             public void run() {
     87                 InstallShortcutReceiver.disableAndFlushInstallQueue(
     88                         InstallShortcutReceiver.FLAG_BULK_ADD, context);
     89             }
     90         });
     91     }
     92 
     93 
     94     /**
     95      * Utility class to help workspace icon addition.
     96      */
     97     public static class UserFolderInfo {
     98 
     99         final ArrayList<ShortcutInfo> pendingShortcuts = new ArrayList<>();
    100 
    101         final UserHandle user;
    102 
    103         final long userSerial;
    104         // Time until which icons will be added to folder instead.
    105         final long addIconToFolderTime;
    106 
    107         final String folderIdKey;
    108         final SharedPreferences prefs;
    109 
    110         final boolean folderAlreadyCreated;
    111         final FolderInfo folderInfo;
    112 
    113         boolean folderPendingAddition;
    114 
    115         public UserFolderInfo(Context context, UserHandle user, BgDataModel dataModel) {
    116             this.user = user;
    117 
    118             UserManagerCompat um = UserManagerCompat.getInstance(context);
    119             userSerial = um.getSerialNumberForUser(user);
    120             addIconToFolderTime = um.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
    121 
    122             folderIdKey = USER_FOLDER_ID_PREFIX + userSerial;
    123             prefs = prefs(context);
    124 
    125             folderAlreadyCreated = prefs.contains(folderIdKey);
    126             if (dataModel != null) {
    127                 if (folderAlreadyCreated) {
    128                     long folderId = prefs.getLong(folderIdKey, ItemInfo.NO_ID);
    129                     folderInfo = dataModel.folders.get(folderId);
    130                 } else {
    131                     folderInfo = new FolderInfo();
    132                     folderInfo.title = context.getText(R.string.work_folder_name);
    133                     folderInfo.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
    134                     folderPendingAddition = true;
    135                 }
    136             } else {
    137                 folderInfo = null;
    138             }
    139         }
    140 
    141         /**
    142          * Returns the ItemInfo which should be added to the workspace. In case the the provided
    143          * {@link ShortcutInfo} or a wrapped {@link FolderInfo} or null.
    144          */
    145         public ItemInfo convertToWorkspaceItem(
    146                 ShortcutInfo shortcut, LauncherActivityInfo activityInfo) {
    147             if (activityInfo.getFirstInstallTime() >= addIconToFolderTime) {
    148                 return shortcut;
    149             }
    150 
    151             if (folderAlreadyCreated) {
    152                 if (folderInfo == null) {
    153                     // Work folder was deleted by user, add icon to home screen.
    154                     return shortcut;
    155                 } else {
    156                     // Add item to work folder instead. Nothing needs to be added
    157                     // on the homescreen.
    158                     pendingShortcuts.add(shortcut);
    159                     return null;
    160                 }
    161             }
    162 
    163             pendingShortcuts.add(shortcut);
    164             folderInfo.add(shortcut, false);
    165             if (folderPendingAddition) {
    166                 folderPendingAddition = false;
    167                 return folderInfo;
    168             } else {
    169                 // WorkFolder already requested to be added. Nothing new needs to be added.
    170                 return null;
    171             }
    172         }
    173 
    174         public void applyPendingState(ModelWriter writer) {
    175             if (folderInfo == null) {
    176                 return;
    177             }
    178 
    179             int startingRank = 0;
    180             if (folderAlreadyCreated) {
    181                 startingRank = folderInfo.contents.size();
    182             }
    183 
    184             for (ShortcutInfo info : pendingShortcuts) {
    185                 info.rank = startingRank++;
    186                 writer.addItemToDatabase(info, folderInfo.id, 0, 0, 0);
    187             }
    188 
    189             if (folderAlreadyCreated) {
    190                 // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
    191                 new MainThreadExecutor().execute(new Runnable() {
    192 
    193                     @Override
    194                     public void run() {
    195                         folderInfo.prepareAutoUpdate();
    196                         for (ShortcutInfo info : pendingShortcuts) {
    197                             folderInfo.add(info, false);
    198                         }
    199                     }
    200                 });
    201             } else {
    202                 prefs.edit().putLong(folderIdKey, folderInfo.id).apply();
    203             }
    204         }
    205     }
    206 
    207     /**
    208      * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
    209      */
    210     public static void processAllUsers(List<UserHandle> users, Context context) {
    211         UserManagerCompat userManager = UserManagerCompat.getInstance(context);
    212         HashSet<String> validKeys = new HashSet<>();
    213         for (UserHandle user : users) {
    214             validKeys.add(USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user));
    215         }
    216 
    217         SharedPreferences prefs = prefs(context);
    218         SharedPreferences.Editor editor = prefs.edit();
    219         for (String key : prefs.getAll().keySet()) {
    220             if (!validKeys.contains(key)) {
    221                 editor.remove(key);
    222             }
    223         }
    224         editor.apply();
    225     }
    226 
    227     /**
    228      * For each user, if a work folder has not been created, mark it such that the folder will
    229      * never get created.
    230      */
    231     public static void markExistingUsersForNoFolderCreation(Context context) {
    232         UserManagerCompat userManager = UserManagerCompat.getInstance(context);
    233         UserHandle myUser = Process.myUserHandle();
    234 
    235         SharedPreferences prefs = null;
    236         for (UserHandle user : userManager.getUserProfiles()) {
    237             if (myUser.equals(user)) {
    238                 continue;
    239             }
    240             if (prefs == null) {
    241                 prefs = prefs(context);
    242             }
    243             String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user);
    244             if (!prefs.contains(folderIdKey)) {
    245                 prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply();
    246             }
    247         }
    248     }
    249 
    250     public static SharedPreferences prefs(Context context) {
    251         return context.getSharedPreferences(
    252                 LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE);
    253     }
    254 }
    255