blob: 0058d65d6fea72a36cf6d9a84c022b223de40c47 [file] [log] [blame]
/*
* Copyright (C) 2017 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.settings.intelligence.suggestions;
import static com.android.settings.intelligence.suggestions.model.SuggestionCategoryRegistry
.CATEGORIES;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.service.settings.suggestions.Suggestion;
import androidx.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.settings.intelligence.overlay.FeatureFactory;
import com.android.settings.intelligence.suggestions.eligibility.CandidateSuggestionFilter;
import com.android.settings.intelligence.suggestions.model.CandidateSuggestion;
import com.android.settings.intelligence.suggestions.model.SuggestionCategory;
import com.android.settings.intelligence.suggestions.model.SuggestionListBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Main parser to get suggestions from all providers.
* <p/>
* Copied from framework/packages/SettingsLib/src/.../SuggestionParser
*/
class SuggestionParser {
private static final String TAG = "SuggestionParser";
private static final String SETUP_TIME = "_setup_time";
private final Context mContext;
private final PackageManager mPackageManager;
private final SharedPreferences mSharedPrefs;
private final Map<String, Suggestion> mAddCache;
public SuggestionParser(Context context) {
mContext = context.getApplicationContext();
mPackageManager = context.getPackageManager();
mAddCache = new ArrayMap<>();
mSharedPrefs = FeatureFactory.get(mContext)
.suggestionFeatureProvider().getSharedPrefs(mContext);
}
public List<Suggestion> getSuggestions() {
final SuggestionListBuilder suggestionBuilder = new SuggestionListBuilder();
for (SuggestionCategory category : CATEGORIES) {
if (category.isExclusive() && !isExclusiveCategoryExpired(category)) {
// If suggestions from an exclusive category are present, parsing is stopped
// and only suggestions from that category are displayed. Note that subsequent
// exclusive categories are also ignored.
// Read suggestion and force ignoreSuggestionDismissRule to be false so the rule
// defined from each suggestion itself is used.
final List<Suggestion> exclusiveSuggestions =
readSuggestions(category, false /* ignoreDismissRule */);
if (!exclusiveSuggestions.isEmpty()) {
suggestionBuilder.addSuggestions(category, exclusiveSuggestions);
return suggestionBuilder.build();
}
} else {
// Either the category is not exclusive, or the exclusiveness expired so we should
// treat it as a normal category.
final List<Suggestion> suggestions =
readSuggestions(category, true /* ignoreDismissRule */);
suggestionBuilder.addSuggestions(category, suggestions);
}
}
return suggestionBuilder.build();
}
@VisibleForTesting
List<Suggestion> readSuggestions(SuggestionCategory category, boolean ignoreDismissRule) {
final List<Suggestion> suggestions = new ArrayList<>();
final Intent probe = new Intent(Intent.ACTION_MAIN);
probe.addCategory(category.getCategory());
List<ResolveInfo> results = mPackageManager
.queryIntentActivities(probe, PackageManager.GET_META_DATA);
// Build a list of eligible candidates
final List<CandidateSuggestion> eligibleCandidates = new ArrayList<>();
for (ResolveInfo resolved : results) {
final CandidateSuggestion candidate = new CandidateSuggestion(mContext, resolved,
ignoreDismissRule);
if (!candidate.isEligible()) {
continue;
}
eligibleCandidates.add(candidate);
}
// Then remove completed ones
final List<CandidateSuggestion> incompleteSuggestions = CandidateSuggestionFilter
.getInstance()
.filterCandidates(mContext, eligibleCandidates);
// Convert the rest to suggestion.
for (CandidateSuggestion candidate : incompleteSuggestions) {
final String id = candidate.getId();
Suggestion suggestion = mAddCache.get(id);
if (suggestion == null) {
suggestion = candidate.toSuggestion();
mAddCache.put(id, suggestion);
}
if (!suggestions.contains(suggestion)) {
suggestions.add(suggestion);
}
}
return suggestions;
}
/**
* Whether or not the category's exclusiveness has expired.
*/
private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
final String keySetupTime = category.getCategory() + SETUP_TIME;
final long currentTime = System.currentTimeMillis();
if (!mSharedPrefs.contains(keySetupTime)) {
mSharedPrefs.edit()
.putLong(keySetupTime, currentTime)
.commit();
}
if (category.getExclusiveExpireDaysInMillis() < 0) {
// negative means never expires
return false;
}
final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
final long elapsedTime = currentTime - setupTime;
Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for "
+ category.getCategory());
return elapsedTime > category.getExclusiveExpireDaysInMillis();
}
}