1 /* 2 * Copyright (C) 2017 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.tv.settings.device.apps.specialaccess; 18 19 import android.app.Application; 20 import android.arch.lifecycle.Lifecycle; 21 import android.arch.lifecycle.LifecycleObserver; 22 import android.content.Context; 23 import android.support.annotation.NonNull; 24 import android.support.v7.preference.Preference; 25 import android.support.v7.preference.PreferenceGroup; 26 27 import com.android.settingslib.applications.ApplicationsState; 28 29 import java.util.ArrayList; 30 import java.util.Comparator; 31 import java.util.List; 32 33 /** 34 * A class to manage a list of apps in a {@link PreferenceGroup}. The list is configured by passing 35 * an {@link ApplicationsState.AppFilter} and a {@link Comparator} for 36 * {@link ApplicationsState.AppEntry} objects, and the PreferenceGroup is manipulated through the 37 * {@link Callback} object. 38 */ 39 public class ManageApplicationsController implements LifecycleObserver { 40 /** 41 * Use this preference key for a header pref not removed during refresh 42 */ 43 public static final String HEADER_KEY = "header"; 44 45 private final Callback mCallback; 46 private final Lifecycle mLifecycle; 47 private final ApplicationsState.AppFilter mFilter; 48 private final Comparator<ApplicationsState.AppEntry> mComparator; 49 50 private ApplicationsState.Session mAppSession; 51 private ApplicationsState mApplicationsState; 52 private final ApplicationsState.Callbacks mAppSessionCallbacks = 53 new ApplicationsState.Callbacks() { 54 55 @Override 56 public void onRunningStateChanged(boolean running) { 57 updateAppList(); 58 } 59 60 @Override 61 public void onPackageListChanged() { 62 updateAppList(); 63 } 64 65 @Override 66 public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) { 67 updateAppList(apps); 68 } 69 70 @Override 71 public void onPackageIconChanged() { 72 updateAppList(); 73 } 74 75 @Override 76 public void onPackageSizeChanged(String packageName) { 77 updateAppList(); 78 } 79 80 @Override 81 public void onAllSizesComputed() { 82 updateAppList(); 83 } 84 85 @Override 86 public void onLauncherInfoChanged() { 87 updateAppList(); 88 } 89 90 @Override 91 public void onLoadEntriesCompleted() { 92 updateAppList(); 93 } 94 }; 95 96 public ManageApplicationsController(@NonNull Context context, @NonNull Callback callback, 97 @NonNull Lifecycle lifecycle, ApplicationsState.AppFilter filter, 98 Comparator<ApplicationsState.AppEntry> comparator) { 99 mCallback = callback; 100 lifecycle.addObserver(this); 101 mLifecycle = lifecycle; 102 mFilter = filter; 103 mComparator = comparator; 104 mApplicationsState = ApplicationsState.getInstance( 105 (Application) context.getApplicationContext()); 106 mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mLifecycle); 107 updateAppList(); 108 } 109 110 /** 111 * Call this method to trigger the app list to refresh. 112 */ 113 public void updateAppList() { 114 ApplicationsState.AppFilter filter = new ApplicationsState.CompoundFilter( 115 mFilter, ApplicationsState.FILTER_NOT_HIDE); 116 ArrayList<ApplicationsState.AppEntry> apps = mAppSession.rebuild(filter, mComparator); 117 if (apps != null) { 118 updateAppList(apps); 119 } 120 } 121 122 private void updateAppList(ArrayList<ApplicationsState.AppEntry> apps) { 123 PreferenceGroup group = mCallback.getAppPreferenceGroup(); 124 final List<Preference> newList = new ArrayList<>(apps.size() + 1); 125 for (final ApplicationsState.AppEntry entry : apps) { 126 final String packageName = entry.info.packageName; 127 mApplicationsState.ensureIcon(entry); 128 Preference recycle = group.findPreference(packageName); 129 if (recycle == null) { 130 recycle = mCallback.createAppPreference(); 131 } 132 newList.add(mCallback.bindPreference(recycle, entry)); 133 } 134 final Preference header = group.findPreference(HEADER_KEY); 135 // Because we're sorting the app entries, we should remove-all to ensure that sort order 136 // is retained 137 group.removeAll(); 138 if (header != null) { 139 group.addPreference(header); 140 } 141 if (newList.size() > 0) { 142 for (Preference prefToAdd : newList) { 143 group.addPreference(prefToAdd); 144 } 145 } else { 146 group.addPreference(mCallback.getEmptyPreference()); 147 } 148 } 149 150 /** 151 * Callback interface for this class to manipulate the list of app preferences. 152 */ 153 public interface Callback { 154 /** 155 * Configure the {@link Preference} object with the data in the 156 * {@link ApplicationsState.AppEntry} 157 * @param preference Preference to configure 158 * @param entry Entry containing data to bind 159 * @return Return the configured Preference object 160 */ 161 @NonNull Preference bindPreference(@NonNull Preference preference, 162 ApplicationsState.AppEntry entry); 163 164 /** 165 * Create a new instance of a {@link Preference} subclass to be used to display an 166 * {@link ApplicationsState.AppEntry} 167 * @return New Preference object 168 */ 169 @NonNull Preference createAppPreference(); 170 171 /** 172 * @return {@link Preference} object to be used as an empty state placeholder 173 */ 174 @NonNull Preference getEmptyPreference(); 175 176 /** 177 * The {@link PreferenceGroup} returned here should contain only app preference objects, 178 * plus optionally a header preference with the key {@link #HEADER_KEY} 179 * @return PreferenceGroup to manipulate 180 */ 181 @NonNull PreferenceGroup getAppPreferenceGroup(); 182 } 183 } 184