blob: 077d2efe092ce47adc1eacf7d1f7edea2bd284dd [file] [log] [blame]
/*
* Copyright (C) 2021 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.tv.settings.device.storage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import com.android.settingslib.applications.ApplicationsState;
import com.android.tv.settings.R;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Manage and retrieve app entries based on current user.
*/
public class AllAppsSession {
private static final String TAG = "AllAppsSession";
private Set<String> mSystemAppPackages;
private OnUpdateAppListListener mOnUpdateAppListListener;
@ApplicationsState.SessionFlags
private static final int SESSION_FLAGS =
ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP
| ApplicationsState.FLAG_SESSION_REQUEST_ICONS
| ApplicationsState.FLAG_SESSION_REQUEST_SIZES
| ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER;
private ApplicationsState mApplicationsState;
private ApplicationsState.Session mSession;
private ApplicationsState.AppFilter mFilterAllApps;
private ApplicationsState.AppFilter mFilterAllAppsExceptPreinstalled;
private final AppFilterType mAppFilterType;
private ArrayList<ApplicationsState.AppEntry> mAllApps = new ArrayList<>();
private static final ApplicationsState.AppFilter PREDEFINED_FILTER_ALL_APPS =
new ApplicationsState.AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(ApplicationsState.AppEntry info) {
return info.info != null;
}
};
private final ApplicationsState.AppFilter mPredefinedFilterAllAppsExceptPreinstalled =
new ApplicationsState.AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(ApplicationsState.AppEntry info) {
return info.info != null
&& info.info.enabled
&& info.hasLauncherEntry
&& info.launcherEntryEnabled
&& !((info.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM)
== ApplicationInfo.PRIVATE_FLAG_OEM)
&& !((info.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR)
== ApplicationInfo.PRIVATE_FLAG_VENDOR)
&& !((info.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT)
== ApplicationInfo.PRIVATE_FLAG_PRODUCT)
&& !mSystemAppPackages.contains(info.info.packageName);
}
};
public enum AppFilterType {
ALL_APPS,
ALL_APPS_EXCEPT_PREINSTALLED
}
public AllAppsSession(Context context, String volumeUuid, String volumeName,
ApplicationsState applicationsState, Lifecycle lifecycle, AppFilterType appFilterType) {
mSystemAppPackages = Arrays.stream(context.getResources()
.getStringArray(R.array.system_app_packages)).collect(Collectors.toSet());
mApplicationsState = applicationsState;
mAppFilterType = appFilterType;
// The UUID of internal storage is null, so we check if there's a volume name to see if we
// should only be showing the apps on the internal storage or all apps.
if (!TextUtils.isEmpty(volumeUuid) || !TextUtils.isEmpty(volumeName)) {
ApplicationsState.AppFilter volumeFilter =
new ApplicationsState.VolumeFilter(volumeUuid);
mFilterAllApps =
new ApplicationsState.CompoundFilter(PREDEFINED_FILTER_ALL_APPS, volumeFilter);
mFilterAllAppsExceptPreinstalled =
new ApplicationsState.CompoundFilter(mPredefinedFilterAllAppsExceptPreinstalled,
volumeFilter);
} else {
mFilterAllApps = PREDEFINED_FILTER_ALL_APPS;
mFilterAllAppsExceptPreinstalled = mPredefinedFilterAllAppsExceptPreinstalled;
}
mSession = mApplicationsState.newSession(new RowUpdateCallbacks(), lifecycle);
mSession.setSessionFlags(SESSION_FLAGS);
}
/**
* A listener to responding app list updating event.
*/
public interface OnUpdateAppListListener {
/**
* It will be called when the app list was updated.
*
* @param entries of the app list.
*/
void onUpdateAppList(@NonNull ArrayList<ApplicationsState.AppEntry> entries);
}
public void setOnUpdateAppListListener(@Nullable OnUpdateAppListListener listener) {
mOnUpdateAppListListener = listener;
}
public ArrayList<ApplicationsState.AppEntry> getAllApps() {
return mAllApps;
}
private ApplicationsState.AppFilter getFilter() {
switch (mAppFilterType) {
case ALL_APPS:
return mFilterAllApps;
case ALL_APPS_EXCEPT_PREINSTALLED:
return mFilterAllAppsExceptPreinstalled;
default:
break;
}
return null;
}
private void rebuildAllApps() {
ArrayList<ApplicationsState.AppEntry> apps =
mSession.rebuild(getFilter(), ApplicationsState.ALPHA_COMPARATOR);
if (apps != null) {
updateAppList(apps);
}
}
private void updateAppList(ArrayList<ApplicationsState.AppEntry> entries) {
mAllApps = filterAppsInstalledInParentProfile(entries);
// Sort the list by app size on descent.
mAllApps.sort((c1, c2) -> (c1.size < c2.size ? 1 : c1.size == c2.size ? 0 : -1));
if (mOnUpdateAppListListener != null) {
mOnUpdateAppListListener.onUpdateAppList(mAllApps);
}
}
private ArrayList<ApplicationsState.AppEntry> filterAppsInstalledInParentProfile(
@Nullable ArrayList<ApplicationsState.AppEntry> appEntries) {
if (appEntries == null) {
return new ArrayList<>();
} else {
return appEntries.stream().filter(appEntry ->
UserHandle.getUserId(appEntry.info.uid) == UserHandle.myUserId())
.collect(Collectors.toCollection(ArrayList::new));
}
}
private class RowUpdateCallbacks implements ApplicationsState.Callbacks {
@Override
public void onRunningStateChanged(boolean running) {
doRebuild();
}
@Override
public void onPackageListChanged() {
doRebuild();
}
@Override
public void onPackageIconChanged() {
doRebuild();
}
@Override
public void onPackageSizeChanged(String packageName) {
doRebuild();
}
@Override
public void onAllSizesComputed() {
doRebuild();
}
@Override
public void onLauncherInfoChanged() {
doRebuild();
}
@Override
public void onLoadEntriesCompleted() {
doRebuild();
}
@Override
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
updateAppList(apps);
}
private void doRebuild() {
rebuildAllApps();
}
}
}