release-request-cacfaf25-4304-4e6b-ab4e-089367c7ed39-for-git_pi-release-4326570 snap-temp-L59400000101056305
Change-Id: Ifac37339de26f970d5519942dc46ded5a507bcda
This commit is contained in:
@@ -9066,4 +9066,11 @@
|
||||
|
||||
<!-- Note displayed when certain features are not available on low ram devices. [CHAR LIMIT=NONE] -->
|
||||
<string name="disabled_low_ram_device">This feature is not available on this device</string>
|
||||
|
||||
<!--Label of IMS registration header -->
|
||||
<string name="ims_reg_title">"IMS registration state"</string>
|
||||
<!--Used when IMS registration state is registered -->
|
||||
<string name="ims_reg_status_registered">"Registered"</string>
|
||||
<!--Used when IMS registration state is not registered -->
|
||||
<string name="ims_reg_status_not_registered">"Not registered"</string>
|
||||
</resources>
|
||||
|
||||
@@ -89,4 +89,10 @@
|
||||
android:title="@string/status_wimax_mac_address"
|
||||
android:summary="@string/device_info_not_available"
|
||||
android:persistent="false" />
|
||||
<Preference
|
||||
android:key="ims_reg_state"
|
||||
android:enabled="false"
|
||||
android:shouldDisableView="false"
|
||||
android:title="@string/ims_reg_title"
|
||||
android:persistent="false" />
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -61,6 +61,7 @@ import android.widget.Switch;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.UnlaunchableAppActivity;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
@@ -152,6 +153,7 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment
|
||||
private int mConfirmingCredentialUser;
|
||||
private IntConsumer mConfirmingCredentialListener;
|
||||
private Set<AdapterData.AliasLoader> mAliasLoaders = new ArraySet<AdapterData.AliasLoader>(2);
|
||||
@GuardedBy("mKeyChainConnectionByProfileId")
|
||||
private final SparseArray<KeyChainConnection>
|
||||
mKeyChainConnectionByProfileId = new SparseArray<KeyChainConnection>();
|
||||
|
||||
@@ -256,11 +258,13 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment
|
||||
}
|
||||
|
||||
private void closeKeyChainConnections() {
|
||||
final int n = mKeyChainConnectionByProfileId.size();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
mKeyChainConnectionByProfileId.valueAt(i).close();
|
||||
synchronized (mKeyChainConnectionByProfileId) {
|
||||
final int n = mKeyChainConnectionByProfileId.size();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
mKeyChainConnectionByProfileId.valueAt(i).close();
|
||||
}
|
||||
mKeyChainConnectionByProfileId.clear();
|
||||
}
|
||||
mKeyChainConnectionByProfileId.clear();
|
||||
}
|
||||
|
||||
private void addTab(Tab tab) {
|
||||
@@ -684,62 +688,64 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment
|
||||
SparseArray<List<CertHolder>> certHoldersByProfile =
|
||||
new SparseArray<List<CertHolder>>();
|
||||
try {
|
||||
List<UserHandle> profiles = mUserManager.getUserProfiles();
|
||||
final int n = profiles.size();
|
||||
// First we get all aliases for all profiles in order to show progress
|
||||
// correctly. Otherwise this could all be in a single loop.
|
||||
SparseArray<List<String>> aliasesByProfileId = new SparseArray<
|
||||
List<String>>(n);
|
||||
int max = 0;
|
||||
int progress = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
UserHandle profile = profiles.get(i);
|
||||
int profileId = profile.getIdentifier();
|
||||
if (shouldSkipProfile(profile)) {
|
||||
continue;
|
||||
synchronized(mKeyChainConnectionByProfileId) {
|
||||
List<UserHandle> profiles = mUserManager.getUserProfiles();
|
||||
final int n = profiles.size();
|
||||
// First we get all aliases for all profiles in order to show progress
|
||||
// correctly. Otherwise this could all be in a single loop.
|
||||
SparseArray<List<String>> aliasesByProfileId = new SparseArray<
|
||||
List<String>>(n);
|
||||
int max = 0;
|
||||
int progress = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
UserHandle profile = profiles.get(i);
|
||||
int profileId = profile.getIdentifier();
|
||||
if (shouldSkipProfile(profile)) {
|
||||
continue;
|
||||
}
|
||||
KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
|
||||
profile);
|
||||
// Saving the connection for later use on the certificate dialog.
|
||||
mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<String> aliases = mTab.getAliases(service);
|
||||
if (isCancelled()) {
|
||||
return new SparseArray<List<CertHolder>>();
|
||||
}
|
||||
max += aliases.size();
|
||||
aliasesByProfileId.put(profileId, aliases);
|
||||
}
|
||||
KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
|
||||
profile);
|
||||
// Saving the connection for later use on the certificate dialog.
|
||||
mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<String> aliases = mTab.getAliases(service);
|
||||
if (isCancelled()) {
|
||||
return new SparseArray<List<CertHolder>>();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
UserHandle profile = profiles.get(i);
|
||||
int profileId = profile.getIdentifier();
|
||||
List<String> aliases = aliasesByProfileId.get(profileId);
|
||||
if (isCancelled()) {
|
||||
return new SparseArray<List<CertHolder>>();
|
||||
}
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
profileId);
|
||||
if (shouldSkipProfile(profile) || aliases == null
|
||||
|| keyChainConnection == null) {
|
||||
certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
|
||||
continue;
|
||||
}
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
|
||||
final int aliasMax = aliases.size();
|
||||
for (int j = 0; j < aliasMax; ++j) {
|
||||
String alias = aliases.get(j);
|
||||
byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
|
||||
true);
|
||||
X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
|
||||
certHolders.add(new CertHolder(service, mAdapter,
|
||||
mTab, alias, cert, profileId));
|
||||
publishProgress(++progress, max);
|
||||
}
|
||||
Collections.sort(certHolders);
|
||||
certHoldersByProfile.put(profileId, certHolders);
|
||||
}
|
||||
max += aliases.size();
|
||||
aliasesByProfileId.put(profileId, aliases);
|
||||
return certHoldersByProfile;
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
UserHandle profile = profiles.get(i);
|
||||
int profileId = profile.getIdentifier();
|
||||
List<String> aliases = aliasesByProfileId.get(profileId);
|
||||
if (isCancelled()) {
|
||||
return new SparseArray<List<CertHolder>>();
|
||||
}
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
profileId);
|
||||
if (shouldSkipProfile(profile) || aliases == null
|
||||
|| keyChainConnection == null) {
|
||||
certHoldersByProfile.put(profileId, new ArrayList<CertHolder>(0));
|
||||
continue;
|
||||
}
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<CertHolder> certHolders = new ArrayList<CertHolder>(max);
|
||||
final int aliasMax = aliases.size();
|
||||
for (int j = 0; j < aliasMax; ++j) {
|
||||
String alias = aliases.get(j);
|
||||
byte[] encodedCertificate = service.getEncodedCaCertificate(alias,
|
||||
true);
|
||||
X509Certificate cert = KeyChain.toCertificate(encodedCertificate);
|
||||
certHolders.add(new CertHolder(service, mAdapter,
|
||||
mTab, alias, cert, profileId));
|
||||
publishProgress(++progress, max);
|
||||
}
|
||||
Collections.sort(certHolders);
|
||||
certHoldersByProfile.put(profileId, certHolders);
|
||||
}
|
||||
return certHoldersByProfile;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Remote exception while loading aliases.", e);
|
||||
return new SparseArray<List<CertHolder>>();
|
||||
@@ -936,16 +942,18 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment
|
||||
public List<X509Certificate> getX509CertsFromCertHolder(CertHolder certHolder) {
|
||||
List<X509Certificate> certificates = null;
|
||||
try {
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
certHolder.mProfileId);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
|
||||
final int n = chain.size();
|
||||
certificates = new ArrayList<X509Certificate>(n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
|
||||
X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
|
||||
certificates.add(certificate);
|
||||
synchronized (mKeyChainConnectionByProfileId) {
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
certHolder.mProfileId);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
List<String> chain = service.getCaCertificateChainAliases(certHolder.mAlias, true);
|
||||
final int n = chain.size();
|
||||
certificates = new ArrayList<X509Certificate>(n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
byte[] encodedCertificate = service.getEncodedCaCertificate(chain.get(i), true);
|
||||
X509Certificate certificate = KeyChain.toCertificate(encodedCertificate);
|
||||
certificates.add(certificate);
|
||||
}
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "RemoteException while retrieving certificate chain for root "
|
||||
@@ -985,15 +993,17 @@ public class TrustedCredentialsSettings extends OptionsMenuFragment
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
mCertHolder.mProfileId);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
if (mCertHolder.mDeleted) {
|
||||
byte[] bytes = mCertHolder.mX509Cert.getEncoded();
|
||||
service.installCaCertificate(bytes);
|
||||
return true;
|
||||
} else {
|
||||
return service.deleteCaCertificate(mCertHolder.mAlias);
|
||||
synchronized (mKeyChainConnectionByProfileId) {
|
||||
KeyChainConnection keyChainConnection = mKeyChainConnectionByProfileId.get(
|
||||
mCertHolder.mProfileId);
|
||||
IKeyChainService service = keyChainConnection.getService();
|
||||
if (mCertHolder.mDeleted) {
|
||||
byte[] bytes = mCertHolder.mX509Cert.getEncoded();
|
||||
service.installCaCertificate(bytes);
|
||||
return true;
|
||||
} else {
|
||||
return service.deleteCaCertificate(mCertHolder.mAlias);
|
||||
}
|
||||
}
|
||||
} catch (CertificateEncodingException | SecurityException | IllegalStateException
|
||||
| RemoteException e) {
|
||||
|
||||
@@ -188,22 +188,27 @@ public final class BluetoothDevicePreference extends GearPreference implements
|
||||
}
|
||||
|
||||
void onClicked() {
|
||||
Context context = getContext();
|
||||
int bondState = mCachedDevice.getBondState();
|
||||
|
||||
final MetricsFeatureProvider metricsFeatureProvider =
|
||||
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider();
|
||||
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
|
||||
|
||||
if (mCachedDevice.isConnected()) {
|
||||
metricsFeatureProvider.action(getContext(),
|
||||
metricsFeatureProvider.action(context,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
|
||||
askDisconnect();
|
||||
} else if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||
metricsFeatureProvider.action(getContext(),
|
||||
metricsFeatureProvider.action(context,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
|
||||
mCachedDevice.connect(true);
|
||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
||||
metricsFeatureProvider.action(getContext(),
|
||||
metricsFeatureProvider.action(context,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
|
||||
if (!mCachedDevice.hasHumanReadableName()) {
|
||||
metricsFeatureProvider.action(context,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
|
||||
}
|
||||
pair();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,19 +94,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener mConditionClickListener = new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Condition condition = (Condition) v.getTag();
|
||||
//TODO: get rid of setTag/getTag
|
||||
mMetricsFeatureProvider.action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_CONDITION_CLICK,
|
||||
condition.getMetricsConstant());
|
||||
condition.onPrimaryClick();
|
||||
}
|
||||
};
|
||||
|
||||
public DashboardAdapter(Context context, Bundle savedInstanceState,
|
||||
List<Condition> conditions, SuggestionParser suggestionParser,
|
||||
SuggestionDismissController.Callback callback) {
|
||||
@@ -129,7 +116,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
|
||||
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
|
||||
suggestionConditionMode = savedInstanceState.getInt(
|
||||
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
|
||||
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
|
||||
mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
|
||||
STATE_SUGGESTIONS_SHOWN_LOGGED);
|
||||
} else {
|
||||
@@ -249,7 +236,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
break;
|
||||
case R.layout.suggestion_condition_container:
|
||||
onBindConditionAndSuggestion(
|
||||
(SuggestionAndConditionContainerHolder) holder, position);
|
||||
(SuggestionAndConditionContainerHolder) holder, position);
|
||||
break;
|
||||
case R.layout.suggestion_condition_header:
|
||||
onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
|
||||
@@ -262,7 +249,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
|
||||
DashboardData prevData = mDashboardData;
|
||||
mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
|
||||
DashboardData.HEADER_MODE_COLLAPSED).build();
|
||||
DashboardData.HEADER_MODE_COLLAPSED).build();
|
||||
notifyDashboardDataChanged(prevData);
|
||||
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
|
||||
});
|
||||
@@ -335,11 +322,11 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
for (Tile suggestion : suggestions) {
|
||||
final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
|
||||
mContext, suggestion);
|
||||
mContext, suggestion);
|
||||
if (!mSuggestionsShownLogged.contains(suggestionId)) {
|
||||
mMetricsFeatureProvider.action(
|
||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
||||
suggestionId);
|
||||
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
|
||||
suggestionId);
|
||||
mSuggestionsShownLogged.add(suggestionId);
|
||||
}
|
||||
}
|
||||
@@ -350,9 +337,9 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
SuggestionConditionHeaderData data) {
|
||||
final int curMode = mDashboardData.getSuggestionConditionMode();
|
||||
final int nextMode = data.hiddenSuggestionCount > 0 && data.conditionCount > 0
|
||||
&& curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
||||
? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
||||
: DashboardData.HEADER_MODE_FULLY_EXPANDED;
|
||||
&& curMode != DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
||||
? DashboardData.HEADER_MODE_SUGGESTION_EXPANDED
|
||||
: DashboardData.HEADER_MODE_FULLY_EXPANDED;
|
||||
final boolean moreSuggestions = data.hiddenSuggestionCount > 0;
|
||||
final boolean hasConditions = data.conditionCount > 0;
|
||||
if (data.conditionCount > 0) {
|
||||
@@ -377,22 +364,22 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
if (curMode == DashboardData.HEADER_MODE_COLLAPSED) {
|
||||
if (data.conditionCount > 0) {
|
||||
holder.summary.setText(mContext.getResources().getQuantityString(
|
||||
R.plurals.suggestions_collapsed_summary,
|
||||
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
|
||||
R.plurals.suggestions_collapsed_summary,
|
||||
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
|
||||
} else {
|
||||
holder.title.setText(mContext.getResources().getQuantityString(
|
||||
R.plurals.suggestions_collapsed_title,
|
||||
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
|
||||
R.plurals.suggestions_collapsed_title,
|
||||
data.hiddenSuggestionCount, data.hiddenSuggestionCount));
|
||||
holder.title.setTextColor(Color.BLACK);
|
||||
holder.summary.setText(null);
|
||||
}
|
||||
} else if (curMode == DashboardData.HEADER_MODE_DEFAULT) {
|
||||
if (data.conditionCount > 0) {
|
||||
holder.summary.setText(mContext.getString(
|
||||
R.string.suggestions_summary, data.hiddenSuggestionCount));
|
||||
R.string.suggestions_summary, data.hiddenSuggestionCount));
|
||||
} else {
|
||||
holder.title.setText(mContext.getString(
|
||||
R.string.suggestions_more_title, data.hiddenSuggestionCount));
|
||||
R.string.suggestions_more_title, data.hiddenSuggestionCount));
|
||||
holder.title.setTextColor(Color.BLACK);
|
||||
holder.summary.setText(null);
|
||||
}
|
||||
@@ -400,7 +387,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
} else if (data.conditionCount > 1) {
|
||||
holder.summary.setTextColor(Utils.getColorAccent(mContext));
|
||||
holder.summary.setText(
|
||||
mContext.getString(R.string.condition_summary, data.conditionCount));
|
||||
mContext.getString(R.string.condition_summary, data.conditionCount));
|
||||
} else {
|
||||
holder.summary.setText(null);
|
||||
}
|
||||
@@ -413,16 +400,16 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
holder.itemView.setPadding(0, padding, 0, padding);
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (moreSuggestions ) {
|
||||
if (moreSuggestions) {
|
||||
logSuggestions();
|
||||
} else if (hasConditions) {
|
||||
mMetricsFeatureProvider.action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
||||
MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, true);
|
||||
}
|
||||
DashboardData prevData = mDashboardData;
|
||||
final boolean wasCollapsed = curMode == DashboardData.HEADER_MODE_COLLAPSED;
|
||||
mDashboardData = new DashboardData.Builder(prevData)
|
||||
.setSuggestionConditionMode(nextMode).build();
|
||||
.setSuggestionConditionMode(nextMode).build();
|
||||
notifyDashboardDataChanged(prevData);
|
||||
if (wasCollapsed) {
|
||||
mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
|
||||
@@ -439,13 +426,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
if (position == SUGGESTION_CONDITION_HEADER_POSITION
|
||||
&& suggestions != null && suggestions.size() > 0) {
|
||||
mSuggestionAdapter = new SuggestionAdapter(mContext, (List<Tile>)
|
||||
mDashboardData.getItemEntityByPosition(position), mSuggestionsShownLogged);
|
||||
mDashboardData.getItemEntityByPosition(position), mSuggestionsShownLogged);
|
||||
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
|
||||
holder.data, mSuggestionParser, mCallback);
|
||||
holder.data, mSuggestionParser, mCallback);
|
||||
holder.data.setAdapter(mSuggestionAdapter);
|
||||
} else {
|
||||
ConditionAdapter adapter = new ConditionAdapter(mContext,
|
||||
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
||||
(List<Condition>) mDashboardData.getItemEntityByPosition(position),
|
||||
mDashboardData.getSuggestionConditionMode());
|
||||
adapter.addDismissHandling(holder.data);
|
||||
holder.data.setAdapter(adapter);
|
||||
@@ -454,19 +441,13 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
|
||||
private void onBindTile(DashboardItemHolder holder, Tile tile) {
|
||||
if (tile.remoteViews != null) {
|
||||
final ViewGroup itemView = (ViewGroup) holder.itemView;
|
||||
itemView.removeAllViews();
|
||||
itemView.addView(tile.remoteViews.apply(itemView.getContext(), itemView));
|
||||
holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
|
||||
holder.title.setText(tile.title);
|
||||
if (!TextUtils.isEmpty(tile.summary)) {
|
||||
holder.summary.setText(tile.summary);
|
||||
holder.summary.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.icon.setImageDrawable(mCache.getIcon(tile.icon));
|
||||
holder.title.setText(tile.title);
|
||||
if (!TextUtils.isEmpty(tile.summary)) {
|
||||
holder.summary.setText(tile.summary);
|
||||
holder.summary.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.summary.setVisibility(View.GONE);
|
||||
}
|
||||
holder.summary.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +488,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
}
|
||||
outState.putStringArrayList(STATE_SUGGESTIONS_SHOWN_LOGGED, mSuggestionsShownLogged);
|
||||
outState.putInt(STATE_SUGGESTION_CONDITION_MODE,
|
||||
mDashboardData.getSuggestionConditionMode());
|
||||
mDashboardData.getSuggestionConditionMode());
|
||||
}
|
||||
|
||||
private void updateConditionIcons(List<Icon> icons, ViewGroup parent) {
|
||||
@@ -519,7 +500,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
|
||||
parent.removeAllViews();
|
||||
for (int i = 1, size = icons.size(); i < size; i++) {
|
||||
ImageView icon = (ImageView) inflater.inflate(
|
||||
R.layout.condition_header_icon, parent, false);
|
||||
R.layout.condition_header_icon, parent, false);
|
||||
icon.setImageIcon(icons.get(i));
|
||||
parent.addView(icon);
|
||||
}
|
||||
|
||||
@@ -171,30 +171,6 @@ public class DashboardData {
|
||||
return POSITION_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of suggestions to display
|
||||
*
|
||||
* The displayable count mainly depends on the {@link #mSuggestionConditionMode}
|
||||
* and the size of suggestions list.
|
||||
*
|
||||
* When in default mode, displayable count couldn't be larger than
|
||||
* {@link #DEFAULT_SUGGESTION_COUNT}.
|
||||
*
|
||||
* When in expanded mode, display all the suggestions.
|
||||
*
|
||||
* @return the count of suggestions to display
|
||||
*/
|
||||
public int getDisplayableSuggestionCount() {
|
||||
final int suggestionSize = sizeOf(mSuggestions);
|
||||
if (mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
|
||||
return 0;
|
||||
}
|
||||
if (mSuggestionConditionMode == HEADER_MODE_DEFAULT) {
|
||||
return Math.min(DEFAULT_SUGGESTION_COUNT, suggestionSize);
|
||||
}
|
||||
return suggestionSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item into list when {@paramref add} is true.
|
||||
*
|
||||
@@ -280,7 +256,7 @@ public class DashboardData {
|
||||
if (conditions == null) {
|
||||
return null;
|
||||
}
|
||||
List<Condition> result = new ArrayList<Condition>();
|
||||
List<Condition> result = new ArrayList<>();
|
||||
final int size = conditions == null ? 0 : conditions.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Condition condition = conditions.get(i);
|
||||
@@ -460,6 +436,17 @@ public class DashboardData {
|
||||
// Only check title and summary for dashboard tile
|
||||
return TextUtils.equals(localTile.title, targetTile.title)
|
||||
&& TextUtils.equals(localTile.summary, targetTile.summary);
|
||||
case TYPE_SUGGESTION_CONDITION_CONTAINER:
|
||||
// If entity is suggestion and contains remote view, force refresh
|
||||
final List entities = (List) entity;
|
||||
if (!entities.isEmpty()) {
|
||||
Object firstEntity = entities.get(0);
|
||||
if (firstEntity instanceof Tile
|
||||
&& ((Tile) firstEntity).remoteViews != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Otherwise Fall through to default
|
||||
default:
|
||||
return entity == null ? targetItem.entity == null
|
||||
: entity.equals(targetItem.entity);
|
||||
@@ -482,7 +469,7 @@ public class DashboardData {
|
||||
conditionCount = sizeOf(conditions);
|
||||
this.hiddenSuggestionCount = hiddenSuggestionCount;
|
||||
title = conditionCount > 0 ? conditions.get(0).getTitle() : null;
|
||||
conditionIcons = new ArrayList<Icon>();
|
||||
conditionIcons = new ArrayList<>();
|
||||
for (int i = 0; conditions != null && i < conditions.size(); i++) {
|
||||
final Condition condition = conditions.get(i);
|
||||
conditionIcons.add(condition.getIcon());
|
||||
|
||||
@@ -230,6 +230,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
||||
|
||||
@VisibleForTesting
|
||||
boolean tintTileIcon(Tile tile) {
|
||||
if (tile.icon == null) {
|
||||
return false;
|
||||
}
|
||||
// First check if the tile has set the icon tintable metadata.
|
||||
final Bundle metadata = tile.metaData;
|
||||
if (metadata != null
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
|
||||
import com.android.settings.dashboard.DashboardAdapter.IconCache;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
import com.android.settingslib.drawer.TileUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -73,6 +75,7 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
|
||||
mSuggestionsShownLogged.add(suggestionId);
|
||||
}
|
||||
if (suggestion.remoteViews != null) {
|
||||
TileUtils.updateTileUsingSummaryUri(mContext, suggestion);
|
||||
final ViewGroup itemView = (ViewGroup) holder.itemView;
|
||||
itemView.removeAllViews();
|
||||
itemView.addView(suggestion.remoteViews.apply(itemView.getContext(), itemView));
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* <p>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
|
||||
*
|
||||
* <p>http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* <p>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.deletionhelper;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
/**
|
||||
* Handles the wall of text which appears below the options in the Storage Management settings drill
|
||||
* down.
|
||||
*/
|
||||
public class AutomaticStorageManagerDescriptionPreferenceController
|
||||
extends AbstractPreferenceController implements PreferenceControllerMixin {
|
||||
private static final String KEY_FREED = "freed_bytes";
|
||||
|
||||
public AutomaticStorageManagerDescriptionPreferenceController(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY_FREED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
Preference preference = screen.findPreference(getPreferenceKey());
|
||||
final Context context = preference.getContext();
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
long freedBytes =
|
||||
Settings.Secure.getLong(
|
||||
cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED, 0);
|
||||
long lastRunMillis =
|
||||
Settings.Secure.getLong(cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN, 0);
|
||||
if (freedBytes == 0 || lastRunMillis == 0 || !isStorageManagerEnabled(cr)) {
|
||||
preference.setSummary(R.string.automatic_storage_manager_text);
|
||||
} else {
|
||||
preference.setSummary(
|
||||
context.getString(
|
||||
R.string.automatic_storage_manager_freed_bytes,
|
||||
Formatter.formatFileSize(context, freedBytes),
|
||||
DateUtils.formatDateTime(
|
||||
context, lastRunMillis, DateUtils.FORMAT_SHOW_DATE)));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStorageManagerEnabled(ContentResolver cr) {
|
||||
return Settings.Secure.getInt(cr, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0)
|
||||
!= 0;
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,13 @@
|
||||
|
||||
package com.android.settings.deletionhelper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.support.v7.preference.DropDownPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -32,39 +30,34 @@ import android.view.ViewGroup;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AutomaticStorageManagerSettings is the Settings screen for configuration and management of the
|
||||
* automatic storage manager.
|
||||
*/
|
||||
public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment
|
||||
public class AutomaticStorageManagerSettings extends DashboardFragment
|
||||
implements OnPreferenceChangeListener {
|
||||
private static final String KEY_DAYS = "days";
|
||||
private static final String KEY_FREED = "freed_bytes";
|
||||
private static final String STORAGE_MANAGER_ENABLED_BY_DEFAULT_PROPERTY =
|
||||
"ro.storage_manager.enabled";
|
||||
|
||||
private AutomaticStorageManagerSwitchBarController mSwitchController;
|
||||
private DropDownPreference mDaysToRetain;
|
||||
private Preference mFreedBytes;
|
||||
private SwitchBar mSwitchBar;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.automatic_storage_management_settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
initializeDaysToRetainPreference();
|
||||
initializeFreedBytesPreference();
|
||||
initializeSwitchBar();
|
||||
|
||||
return view;
|
||||
@@ -98,35 +91,25 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment
|
||||
getFragmentManager());
|
||||
}
|
||||
|
||||
private void initializeFreedBytesPreference() {
|
||||
ContentResolver cr = getContentResolver();
|
||||
mFreedBytes = findPreference(KEY_FREED);
|
||||
long freedBytes = Settings.Secure.getLong(cr,
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
|
||||
0);
|
||||
long lastRunMillis = Settings.Secure.getLong(cr,
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
|
||||
0);
|
||||
if (freedBytes == 0 || lastRunMillis == 0) {
|
||||
mFreedBytes.setVisible(false);
|
||||
} else {
|
||||
final Activity activity = getActivity();
|
||||
mFreedBytes.setSummary(
|
||||
activity.getString(
|
||||
R.string.automatic_storage_manager_freed_bytes,
|
||||
Formatter.formatFileSize(activity, freedBytes),
|
||||
DateUtils.formatDateTime(
|
||||
activity, lastRunMillis, DateUtils.FORMAT_SHOW_DATE)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
boolean isStorageManagerChecked =
|
||||
Settings.Secure.getInt(getContentResolver(),
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0;
|
||||
mDaysToRetain.setEnabled(isStorageManagerChecked);
|
||||
mDaysToRetain.setEnabled(isStorageManagerEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.automatic_storage_management_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
|
||||
return buildPreferenceControllers(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,4 +151,30 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment
|
||||
return indices.length - 1;
|
||||
}
|
||||
|
||||
private boolean isStorageManagerEnabled() {
|
||||
return Settings.Secure.getInt(
|
||||
getContentResolver(), Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0)
|
||||
!= 0;
|
||||
}
|
||||
|
||||
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
controllers.add(new AutomaticStorageManagerDescriptionPreferenceController(context));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
/** For Search. */
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
protected boolean isPageSearchEnabled(Context context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AbstractPreferenceController> getPreferenceControllers(
|
||||
Context context) {
|
||||
return buildPreferenceControllers(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.StrictMode;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.IStorageManager;
|
||||
import android.provider.SearchIndexableResource;
|
||||
@@ -1029,8 +1030,19 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
|
||||
return context.getSystemService(Context.OEM_LOCK_SERVICE) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether OEM unlock is allowed by the user and carrier.
|
||||
*
|
||||
* This does not take into account any restrictions imposed by the device policy.
|
||||
*/
|
||||
private boolean isOemUnlockAllowedByUserAndCarrier() {
|
||||
final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
|
||||
return mOemLockManager.isOemUnlockAllowedByCarrier()
|
||||
&& !mUm.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle);
|
||||
}
|
||||
|
||||
private boolean enableOemUnlockPreference() {
|
||||
return !isBootloaderUnlocked() && mOemLockManager.canUserAllowOemUnlock();
|
||||
return !isBootloaderUnlocked() && isOemUnlockAllowedByUserAndCarrier();
|
||||
}
|
||||
|
||||
private void updateOemUnlockOptions() {
|
||||
@@ -1044,10 +1056,6 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
|
||||
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
|
||||
mEnableOemUnlock.checkRestrictionAndSetDisabled(UserManager.DISALLOW_FACTORY_RESET);
|
||||
}
|
||||
if (mEnableOemUnlock.isEnabled()) {
|
||||
// Check restriction, disable mEnableOemUnlock and apply policy transparency.
|
||||
mEnableOemUnlock.checkRestrictionAndSetDisabled(UserManager.DISALLOW_OEM_UNLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2572,7 +2580,7 @@ public class DevelopmentSettings extends RestrictedSettingsFragment
|
||||
oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_bootloader_unlocked;
|
||||
} else if (isSimLockedDevice()) {
|
||||
oemUnlockSummary = R.string.oem_unlock_enable_disabled_summary_sim_locked_device;
|
||||
} else if (!mOemLockManager.canUserAllowOemUnlock()) {
|
||||
} else if (!isOemUnlockAllowedByUserAndCarrier()) {
|
||||
// If the device isn't SIM-locked but OEM unlock is disallowed by some party, this
|
||||
// means either some other carrier restriction is in place or the device hasn't been
|
||||
// able to confirm which restrictions (SIM-lock or otherwise) apply.
|
||||
|
||||
@@ -28,11 +28,15 @@ import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserManager;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
@@ -56,6 +60,7 @@ public class Status extends SettingsPreferenceFragment {
|
||||
private static final String KEY_WIMAX_MAC_ADDRESS = "wimax_mac_address";
|
||||
private static final String KEY_SIM_STATUS = "sim_status";
|
||||
private static final String KEY_IMEI_INFO = "imei_info";
|
||||
private static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state";
|
||||
|
||||
// Broadcasts to listen to for connectivity changes.
|
||||
private static final String[] CONNECTIVITY_INTENTS = {
|
||||
@@ -85,6 +90,8 @@ public class Status extends SettingsPreferenceFragment {
|
||||
private Preference mIpAddress;
|
||||
private Preference mWifiMacAddress;
|
||||
private Preference mWimaxMacAddress;
|
||||
private Preference mImsStatus;
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
private static class MyHandler extends Handler {
|
||||
@@ -162,6 +169,7 @@ public class Status extends SettingsPreferenceFragment {
|
||||
mWifiMacAddress = findPreference(KEY_WIFI_MAC_ADDRESS);
|
||||
mWimaxMacAddress = findPreference(KEY_WIMAX_MAC_ADDRESS);
|
||||
mIpAddress = findPreference(KEY_IP_ADDRESS);
|
||||
mImsStatus = findPreference(KEY_IMS_REGISTRATION_STATE);
|
||||
|
||||
mRes = getResources();
|
||||
mUnavailable = mRes.getString(R.string.status_unavailable);
|
||||
@@ -269,11 +277,31 @@ public class Status extends SettingsPreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void setImsRegistrationStatus() {
|
||||
CarrierConfigManager configManager = (CarrierConfigManager)
|
||||
getSystemService(Context.CARRIER_CONFIG_SERVICE);
|
||||
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
|
||||
PersistableBundle config = null;
|
||||
if (configManager != null) {
|
||||
config = configManager.getConfigForSubId(subId);
|
||||
}
|
||||
if (config != null && config.getBoolean(
|
||||
CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL)) {
|
||||
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
|
||||
mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ?
|
||||
R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered);
|
||||
} else {
|
||||
removePreferenceFromScreen(KEY_IMS_REGISTRATION_STATE);
|
||||
mImsStatus = null;
|
||||
}
|
||||
}
|
||||
|
||||
void updateConnectivity() {
|
||||
setWimaxStatus();
|
||||
setWifiStatus();
|
||||
setBtStatus();
|
||||
setIpAddressStatus();
|
||||
setImsRegistrationStatus();
|
||||
}
|
||||
|
||||
void updateTimes() {
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.android.settings.applications.UserManagerWrapper;
|
||||
import com.android.settings.applications.UserManagerWrapperImpl;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
|
||||
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
|
||||
import com.android.settings.deviceinfo.storage.SecondaryUserController;
|
||||
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
|
||||
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
|
||||
@@ -68,6 +69,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
||||
private VolumeInfo mVolume;
|
||||
private PrivateStorageInfo mStorageInfo;
|
||||
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
|
||||
private CachedStorageValuesHelper mCachedStorageValuesHelper;
|
||||
|
||||
private StorageSummaryDonutPreferenceController mSummaryController;
|
||||
private StorageItemPreferenceController mPreferenceController;
|
||||
@@ -102,7 +104,10 @@ public class StorageDashboardFragment extends DashboardFragment
|
||||
@Override
|
||||
public void onViewCreated(View v, Bundle savedInstanceState) {
|
||||
super.onViewCreated(v, savedInstanceState);
|
||||
setLoading(true, false);
|
||||
initializeCacheProvider();
|
||||
if (mAppsResult == null || mStorageInfo == null) {
|
||||
setLoading(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -249,6 +254,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
||||
public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
|
||||
mAppsResult = data;
|
||||
maybeCacheFreshValues();
|
||||
onReceivedSizes();
|
||||
}
|
||||
|
||||
@@ -256,6 +262,48 @@ public class StorageDashboardFragment extends DashboardFragment
|
||||
public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
|
||||
mCachedStorageValuesHelper = helper;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public PrivateStorageInfo getPrivateStorageInfo() {
|
||||
return mStorageInfo;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public SparseArray<StorageAsyncLoader.AppsStorageResult> getAppsStorageResult() {
|
||||
return mAppsResult;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void initializeCachedValues() {
|
||||
PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
|
||||
mCachedStorageValuesHelper.getCachedAppsStorageResult();
|
||||
if (info == null || loaderResult == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStorageInfo = info;
|
||||
mAppsResult = loaderResult;
|
||||
}
|
||||
|
||||
private void initializeCacheProvider() {
|
||||
mCachedStorageValuesHelper =
|
||||
new CachedStorageValuesHelper(getContext(), UserHandle.myUserId());
|
||||
initializeCachedValues();
|
||||
onReceivedSizes();
|
||||
}
|
||||
|
||||
private void maybeCacheFreshValues() {
|
||||
if (mStorageInfo != null && mAppsResult != null) {
|
||||
mCachedStorageValuesHelper.cacheResult(
|
||||
mStorageInfo, mAppsResult.get(UserHandle.myUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IconLoaderCallbacks exists because StorageDashboardFragment already implements
|
||||
* LoaderCallbacks for a different type.
|
||||
@@ -308,6 +356,7 @@ public class StorageDashboardFragment extends DashboardFragment
|
||||
}
|
||||
|
||||
mStorageInfo = privateStorageInfo;
|
||||
maybeCacheFreshValues();
|
||||
onReceivedSizes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.deviceinfo.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.settingslib.applications.StorageStatsSource;
|
||||
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CachedStorageValuesHelper {
|
||||
|
||||
@VisibleForTesting public static final String SHARED_PREFERENCES_NAME = "CachedStorageValues";
|
||||
public static final String TIMESTAMP_KEY = "last_query_timestamp";
|
||||
public static final String FREE_BYTES_KEY = "free_bytes";
|
||||
public static final String TOTAL_BYTES_KEY = "total_bytes";
|
||||
public static final String GAME_APPS_SIZE_KEY = "game_apps_size";
|
||||
public static final String MUSIC_APPS_SIZE_KEY = "music_apps_size";
|
||||
public static final String VIDEO_APPS_SIZE_KEY = "video_apps_size";
|
||||
public static final String PHOTO_APPS_SIZE_KEY = "photo_apps_size";
|
||||
public static final String OTHER_APPS_SIZE_KEY = "other_apps_size";
|
||||
public static final String CACHE_APPS_SIZE_KEY = "cache_apps_size";
|
||||
public static final String EXTERNAL_TOTAL_BYTES = "external_total_bytes";
|
||||
public static final String EXTERNAL_AUDIO_BYTES = "external_audio_bytes";
|
||||
public static final String EXTERNAL_VIDEO_BYTES = "external_video_bytes";
|
||||
public static final String EXTERNAL_IMAGE_BYTES = "external_image_bytes";
|
||||
public static final String EXTERNAL_APP_BYTES = "external_apps_bytes";
|
||||
public static final String USER_ID_KEY = "user_id";
|
||||
private final Long mClobberThreshold;
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
private final int mUserId;
|
||||
// This clock is used to provide the time. By default, it uses the system clock, but can be
|
||||
// replaced for test purposes.
|
||||
protected Clock mClock;
|
||||
|
||||
public CachedStorageValuesHelper(Context context, int userId) {
|
||||
mSharedPreferences =
|
||||
context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
mClock = new Clock();
|
||||
mUserId = userId;
|
||||
mClobberThreshold =
|
||||
Settings.Global.getLong(
|
||||
context.getContentResolver(),
|
||||
Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
|
||||
TimeUnit.MINUTES.toMillis(5));
|
||||
}
|
||||
|
||||
public PrivateStorageInfo getCachedPrivateStorageInfo() {
|
||||
if (!isDataValid()) {
|
||||
return null;
|
||||
}
|
||||
final long freeBytes = mSharedPreferences.getLong(FREE_BYTES_KEY, -1);
|
||||
final long totalBytes = mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1);
|
||||
if (freeBytes < 0 || totalBytes < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PrivateStorageInfo(freeBytes, totalBytes);
|
||||
}
|
||||
|
||||
public SparseArray<StorageAsyncLoader.AppsStorageResult> getCachedAppsStorageResult() {
|
||||
if (!isDataValid()) {
|
||||
return null;
|
||||
}
|
||||
final long gamesSize = mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1);
|
||||
final long musicAppsSize = mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1);
|
||||
final long videoAppsSize = mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1);
|
||||
final long photoAppSize = mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1);
|
||||
final long otherAppsSize = mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1);
|
||||
final long cacheSize = mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1);
|
||||
if (gamesSize < 0
|
||||
|| musicAppsSize < 0
|
||||
|| videoAppsSize < 0
|
||||
|| photoAppSize < 0
|
||||
|| otherAppsSize < 0
|
||||
|| cacheSize < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final long externalTotalBytes = mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1);
|
||||
final long externalAudioBytes = mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1);
|
||||
final long externalVideoBytes = mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1);
|
||||
final long externalImageBytes = mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1);
|
||||
final long externalAppBytes = mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1);
|
||||
if (externalTotalBytes < 0
|
||||
|| externalAudioBytes < 0
|
||||
|| externalVideoBytes < 0
|
||||
|| externalImageBytes < 0
|
||||
|| externalAppBytes < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StorageStatsSource.ExternalStorageStats externalStats =
|
||||
new StorageStatsSource.ExternalStorageStats(
|
||||
externalTotalBytes,
|
||||
externalAudioBytes,
|
||||
externalVideoBytes,
|
||||
externalImageBytes,
|
||||
externalAppBytes);
|
||||
final StorageAsyncLoader.AppsStorageResult result =
|
||||
new StorageAsyncLoader.AppsStorageResult();
|
||||
result.gamesSize = gamesSize;
|
||||
result.musicAppsSize = musicAppsSize;
|
||||
result.videoAppsSize = videoAppsSize;
|
||||
result.photosAppsSize = photoAppSize;
|
||||
result.otherAppsSize = otherAppsSize;
|
||||
result.cacheSize = cacheSize;
|
||||
result.externalStats = externalStats;
|
||||
final SparseArray<StorageAsyncLoader.AppsStorageResult> resultArray = new SparseArray<>();
|
||||
resultArray.append(mUserId, result);
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
public void cacheResult(
|
||||
PrivateStorageInfo storageInfo, StorageAsyncLoader.AppsStorageResult result) {
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(FREE_BYTES_KEY, storageInfo.freeBytes)
|
||||
.putLong(TOTAL_BYTES_KEY, storageInfo.totalBytes)
|
||||
.putLong(GAME_APPS_SIZE_KEY, result.gamesSize)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, result.musicAppsSize)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, result.videoAppsSize)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, result.photosAppsSize)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, result.otherAppsSize)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, result.cacheSize)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, result.externalStats.totalBytes)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, result.externalStats.audioBytes)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, result.externalStats.videoBytes)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, result.externalStats.imageBytes)
|
||||
.putLong(EXTERNAL_APP_BYTES, result.externalStats.appBytes)
|
||||
.putInt(USER_ID_KEY, mUserId)
|
||||
.putLong(TIMESTAMP_KEY, mClock.getCurrentTime())
|
||||
.apply();
|
||||
}
|
||||
|
||||
private boolean isDataValid() {
|
||||
final int cachedUserId = mSharedPreferences.getInt(USER_ID_KEY, -1);
|
||||
if (cachedUserId != mUserId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final long lastQueryTime = mSharedPreferences.getLong(TIMESTAMP_KEY, Long.MAX_VALUE);
|
||||
final long currentTime = mClock.getCurrentTime();
|
||||
return currentTime - lastQueryTime < mClobberThreshold;
|
||||
}
|
||||
|
||||
/** Clock provides the current time. */
|
||||
static class Clock {
|
||||
public long getCurrentTime() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,23 +208,18 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
|
||||
public void setUserId(UserHandle userHandle) {
|
||||
mUserId = userHandle.getIdentifier();
|
||||
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
badgePreference(pm, userHandle, mPhotoPreference);
|
||||
badgePreference(pm, userHandle, mMoviesPreference);
|
||||
badgePreference(pm, userHandle, mAudioPreference);
|
||||
badgePreference(pm, userHandle, mGamePreference);
|
||||
badgePreference(pm, userHandle, mAppPreference);
|
||||
badgePreference(pm, userHandle, mSystemPreference);
|
||||
badgePreference(pm, userHandle, mFilePreference);
|
||||
tintPreference(mPhotoPreference);
|
||||
tintPreference(mMoviesPreference);
|
||||
tintPreference(mAudioPreference);
|
||||
tintPreference(mGamePreference);
|
||||
tintPreference(mAppPreference);
|
||||
tintPreference(mSystemPreference);
|
||||
tintPreference(mFilePreference);
|
||||
}
|
||||
|
||||
private void badgePreference(PackageManager pm, UserHandle userHandle, Preference preference) {
|
||||
private void tintPreference(Preference preference) {
|
||||
if (preference != null) {
|
||||
Drawable currentIcon = preference.getIcon();
|
||||
// Sigh... Applying the badge to the icon clobbers the tint on the base drawable.
|
||||
// For some reason, reapplying it here means the tint remains.
|
||||
currentIcon = applyTint(mContext, currentIcon);
|
||||
preference.setIcon(pm.getUserBadgedIcon(currentIcon, userHandle));
|
||||
preference.setIcon(applyTint(mContext, preference.getIcon()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,27 +17,6 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
|
||||
import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
|
||||
import static com.android.settings.search.DatabaseResultLoader
|
||||
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
|
||||
@@ -70,17 +49,14 @@ import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.USER_
|
||||
import static com.android.settings.search.IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.provider.SearchIndexableData;
|
||||
@@ -89,7 +65,6 @@ import android.provider.SearchIndexablesContract;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
@@ -98,13 +73,13 @@ import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
|
||||
import com.android.settings.search.indexing.IndexableDataCollector;
|
||||
import com.android.settings.search.indexing.PreIndexData;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -132,23 +107,14 @@ public class DatabaseIndexingManager {
|
||||
private static final String NODE_NAME_CHECK_BOX_PREFERENCE = "CheckBoxPreference";
|
||||
private static final String NODE_NAME_LIST_PREFERENCE = "ListPreference";
|
||||
|
||||
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
||||
|
||||
private final String mBaseAuthority;
|
||||
|
||||
@VisibleForTesting
|
||||
final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false);
|
||||
|
||||
@VisibleForTesting
|
||||
final UpdateData mDataToProcess = new UpdateData();
|
||||
private IndexableDataCollector mCollector;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public DatabaseIndexingManager(Context context, String baseAuthority) {
|
||||
mContext = context;
|
||||
mBaseAuthority = baseAuthority;
|
||||
}
|
||||
|
||||
public void setContext(Context context) {
|
||||
public DatabaseIndexingManager(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@@ -177,33 +143,17 @@ public class DatabaseIndexingManager {
|
||||
final String providerVersionedNames =
|
||||
IndexDatabaseHelper.buildProviderVersionedNames(providers);
|
||||
|
||||
final boolean isFullIndex = IndexDatabaseHelper.isFullIndex(mContext, localeStr,
|
||||
final boolean isFullIndex = isFullIndex(mContext, localeStr,
|
||||
fingerprint, providerVersionedNames);
|
||||
|
||||
if (isFullIndex) {
|
||||
rebuildDatabase();
|
||||
}
|
||||
|
||||
for (final ResolveInfo info : providers) {
|
||||
if (!DatabaseIndexingUtils.isWellKnownProvider(info, mContext)) {
|
||||
continue;
|
||||
}
|
||||
final String authority = info.providerInfo.authority;
|
||||
final String packageName = info.providerInfo.packageName;
|
||||
PreIndexData indexData = getIndexDataFromProviders(providers, isFullIndex);
|
||||
|
||||
if (isFullIndex) {
|
||||
addIndexablesFromRemoteProvider(packageName, authority);
|
||||
}
|
||||
final long nonIndexableStartTime = System.currentTimeMillis();
|
||||
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||
if (SettingsSearchIndexablesProvider.DEBUG) {
|
||||
final long nonIndextableTime = System.currentTimeMillis() - nonIndexableStartTime;
|
||||
Log.d(LOG_TAG, "performIndexing update non-indexable for package " + packageName
|
||||
+ " took time: " + nonIndextableTime);
|
||||
}
|
||||
}
|
||||
final long updateDatabaseStartTime = System.currentTimeMillis();
|
||||
updateDatabase(isFullIndex, localeStr);
|
||||
updateDatabase(indexData, isFullIndex, localeStr);
|
||||
if (SettingsSearchIndexablesProvider.DEBUG) {
|
||||
final long updateDatabaseTime = System.currentTimeMillis() - updateDatabaseStartTime;
|
||||
Log.d(LOG_TAG, "performIndexing updateDatabase took time: " + updateDatabaseTime);
|
||||
@@ -221,10 +171,37 @@ public class DatabaseIndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PreIndexData getIndexDataFromProviders(List<ResolveInfo> providers, boolean isFullIndex) {
|
||||
if (mCollector == null) {
|
||||
mCollector = new IndexableDataCollector(mContext);
|
||||
}
|
||||
return mCollector.collectIndexableData(providers, isFullIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconstruct the database in the following cases:
|
||||
* - Language has changed
|
||||
* - Build has changed
|
||||
* Checks if the indexed data is obsolete, when either:
|
||||
* - Device language has changed
|
||||
* - Device has taken an OTA.
|
||||
* In both cases, the device requires a full index.
|
||||
*
|
||||
* @param locale is the default for the device
|
||||
* @param fingerprint id for the current build.
|
||||
* @return true if a full index should be preformed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean isFullIndex(Context context, String locale, String fingerprint,
|
||||
String providerVersionedNames) {
|
||||
final boolean isLocaleIndexed = IndexDatabaseHelper.isLocaleAlreadyIndexed(context, locale);
|
||||
final boolean isBuildIndexed = IndexDatabaseHelper.isBuildIndexed(context, fingerprint);
|
||||
final boolean areProvidersIndexed = IndexDatabaseHelper
|
||||
.areProvidersIndexed(context, providerVersionedNames);
|
||||
|
||||
return !(isLocaleIndexed && isBuildIndexed && areProvidersIndexed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the currently stored database, and clear the flags which mark the database as indexed.
|
||||
*/
|
||||
private void rebuildDatabase() {
|
||||
// Drop the database when the locale or build has changed. This eliminates rows which are
|
||||
@@ -244,16 +221,9 @@ public class DatabaseIndexingManager {
|
||||
* @param localeStr the default locale for the device.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updateDatabase(boolean needsReindexing, String localeStr) {
|
||||
final UpdateData copy;
|
||||
|
||||
synchronized (mDataToProcess) {
|
||||
copy = mDataToProcess.copy();
|
||||
mDataToProcess.clear();
|
||||
}
|
||||
|
||||
final List<SearchIndexableData> dataToUpdate = copy.dataToUpdate;
|
||||
final Map<String, Set<String>> nonIndexableKeys = copy.nonIndexableKeys;
|
||||
void updateDatabase(PreIndexData indexData, boolean needsReindexing, String localeStr) {
|
||||
final List<SearchIndexableData> dataToUpdate = indexData.dataToUpdate;
|
||||
final Map<String, Set<String>> nonIndexableKeys = indexData.nonIndexableKeys;
|
||||
|
||||
final SQLiteDatabase database = getWritableDatabase();
|
||||
if (database == null) {
|
||||
@@ -378,99 +348,10 @@ public class DatabaseIndexingManager {
|
||||
disabledResults.close();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
||||
try {
|
||||
final Context context = mBaseAuthority.equals(authority) ?
|
||||
mContext : mContext.createPackageContext(packageName, 0);
|
||||
|
||||
final Uri uriForResources = buildUriForXmlResources(authority);
|
||||
addIndexablesForXmlResourceUri(context, packageName, uriForResources,
|
||||
SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS);
|
||||
|
||||
final Uri uriForRawData = buildUriForRawData(authority);
|
||||
addIndexablesForRawDataUri(context, packageName, uriForRawData,
|
||||
SearchIndexablesContract.INDEXABLES_RAW_COLUMNS);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
|
||||
+ Log.getStackTraceString(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||
String authority) {
|
||||
final List<String> keys =
|
||||
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||
|
||||
addNonIndexableKeys(packageName, keys);
|
||||
}
|
||||
|
||||
private List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||
String authority) {
|
||||
try {
|
||||
final Context packageContext = mContext.createPackageContext(packageName, 0);
|
||||
|
||||
final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority);
|
||||
return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys,
|
||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(LOG_TAG, "Could not create context for " + packageName + ": "
|
||||
+ Log.getStackTraceString(e));
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getNonIndexablesKeys(Context packageContext, Uri uri,
|
||||
String[] projection) {
|
||||
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
|
||||
final List<String> result = new ArrayList<>();
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE);
|
||||
|
||||
if (TextUtils.isEmpty(key) && Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
|
||||
Log.v(LOG_TAG, "Empty non-indexable key from: "
|
||||
+ packageContext.getPackageName());
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void addIndexableData(SearchIndexableData data) {
|
||||
synchronized (mDataToProcess) {
|
||||
mDataToProcess.dataToUpdate.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
public void addNonIndexableKeys(String authority, List<String> keys) {
|
||||
synchronized (mDataToProcess) {
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
mDataToProcess.nonIndexableKeys.put(authority, new ArraySet<>(keys));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (b/64951285): Deprecate this method
|
||||
*
|
||||
* Update the Index for a specific class name resources
|
||||
*
|
||||
* @param className the class name (typically a fragment name).
|
||||
@@ -491,9 +372,9 @@ public class DatabaseIndexingManager {
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addIndexableData(res);
|
||||
updateDatabase(false, Locale.getDefault().toString());
|
||||
res.enabled = false;
|
||||
// addIndexableData(res);
|
||||
// updateDatabase(false, Locale.getDefault().toString());
|
||||
// res.enabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -507,126 +388,9 @@ public class DatabaseIndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static Uri buildUriForXmlResources(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
|
||||
}
|
||||
|
||||
private static Uri buildUriForRawData(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.INDEXABLES_RAW_PATH);
|
||||
}
|
||||
|
||||
private static Uri buildUriForNonIndexableKeys(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
||||
}
|
||||
|
||||
private void addIndexablesForXmlResourceUri(Context packageContext, String packageName,
|
||||
Uri uri, String[] projection) {
|
||||
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
|
||||
|
||||
final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
|
||||
final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);
|
||||
|
||||
final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);
|
||||
final String targetPackage = cursor.getString(
|
||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);
|
||||
final String targetClass = cursor.getString(
|
||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
|
||||
|
||||
SearchIndexableResource sir = new SearchIndexableResource(packageContext);
|
||||
sir.xmlResId = xmlResId;
|
||||
sir.className = className;
|
||||
sir.packageName = packageName;
|
||||
sir.iconResId = iconResId;
|
||||
sir.intentAction = action;
|
||||
sir.intentTargetPackage = targetPackage;
|
||||
sir.intentTargetClass = targetClass;
|
||||
|
||||
addIndexableData(sir);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void addIndexablesForRawDataUri(Context packageContext, String packageName,
|
||||
Uri uri, String[] projection) {
|
||||
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(LOG_TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
|
||||
// TODO Remove rank
|
||||
final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
|
||||
final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
|
||||
final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
|
||||
final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES);
|
||||
final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS);
|
||||
|
||||
final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE);
|
||||
|
||||
final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME);
|
||||
final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID);
|
||||
|
||||
final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION);
|
||||
final String targetPackage = cursor.getString(
|
||||
COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE);
|
||||
final String targetClass = cursor.getString(
|
||||
COLUMN_INDEX_RAW_INTENT_TARGET_CLASS);
|
||||
|
||||
final String key = cursor.getString(COLUMN_INDEX_RAW_KEY);
|
||||
final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
|
||||
|
||||
SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
|
||||
data.title = title;
|
||||
data.summaryOn = summaryOn;
|
||||
data.summaryOff = summaryOff;
|
||||
data.entries = entries;
|
||||
data.keywords = keywords;
|
||||
data.screenTitle = screenTitle;
|
||||
data.className = className;
|
||||
data.packageName = packageName;
|
||||
data.iconResId = iconResId;
|
||||
data.intentAction = action;
|
||||
data.intentTargetPackage = targetPackage;
|
||||
data.intentTargetClass = targetClass;
|
||||
data.key = key;
|
||||
data.userId = userId;
|
||||
|
||||
addIndexableData(data);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
||||
@VisibleForTesting
|
||||
void indexOneSearchIndexableData(SQLiteDatabase database, String localeStr,
|
||||
SearchIndexableData data, Map<String, Set<String>> nonIndexableKeys) {
|
||||
if (data instanceof SearchIndexableResource) {
|
||||
indexOneResource(database, localeStr, (SearchIndexableResource) data, nonIndexableKeys);
|
||||
@@ -1010,38 +774,6 @@ public class DatabaseIndexingManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private class to describe the indexDatabase data for the Index database
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
static class UpdateData {
|
||||
public List<SearchIndexableData> dataToUpdate;
|
||||
public List<SearchIndexableData> dataToDisable;
|
||||
public Map<String, Set<String>> nonIndexableKeys;
|
||||
|
||||
public UpdateData() {
|
||||
dataToUpdate = new ArrayList<>();
|
||||
dataToDisable = new ArrayList<>();
|
||||
nonIndexableKeys = new HashMap<>();
|
||||
}
|
||||
|
||||
public UpdateData(UpdateData other) {
|
||||
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
||||
dataToDisable = new ArrayList<>(other.dataToDisable);
|
||||
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
||||
}
|
||||
|
||||
public UpdateData copy() {
|
||||
return new UpdateData(this);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
dataToUpdate.clear();
|
||||
dataToDisable.clear();
|
||||
nonIndexableKeys.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DatabaseRow {
|
||||
public final String locale;
|
||||
public final String updatedTitle;
|
||||
|
||||
@@ -174,35 +174,6 @@ public class DatabaseIndexingUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow a "well known" SearchIndexablesProvider. The provider should:
|
||||
*
|
||||
* - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES}
|
||||
* - be from a privileged package
|
||||
*/
|
||||
static boolean isWellKnownProvider(ResolveInfo info, Context context) {
|
||||
final String authority = info.providerInfo.authority;
|
||||
final String packageName = info.providerInfo.applicationInfo.packageName;
|
||||
|
||||
if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String readPermission = info.providerInfo.readPermission;
|
||||
final String writePermission = info.providerInfo.writePermission;
|
||||
|
||||
if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) ||
|
||||
!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isPrivilegedPackage(packageName, context);
|
||||
}
|
||||
|
||||
static String normalizeHyphen(String input) {
|
||||
return (input != null) ? input.replaceAll(NON_BREAKING_HYPHEN, HYPHEN) : EMPTY;
|
||||
}
|
||||
@@ -217,15 +188,4 @@ public class DatabaseIndexingUtils {
|
||||
static String normalizeKeywords(String input) {
|
||||
return (input != null) ? input.replaceAll(LIST_DELIMITERS, SPACE) : EMPTY;
|
||||
}
|
||||
|
||||
private static boolean isPrivilegedPackage(String packageName, Context context) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
PackageInfo packInfo = pm.getPackageInfo(packageName, 0);
|
||||
return ((packInfo.applicationInfo.privateFlags
|
||||
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,19 +40,19 @@ public class DatabaseResultLoader extends AsyncLoader<Set<? extends SearchResult
|
||||
/* These indices are used to match the columns of the this loader's SELECT statement.
|
||||
These are not necessarily the same order nor similar coverage as the schema defined in
|
||||
IndexDatabaseHelper */
|
||||
static final int COLUMN_INDEX_ID = 0;
|
||||
static final int COLUMN_INDEX_TITLE = 1;
|
||||
static final int COLUMN_INDEX_SUMMARY_ON = 2;
|
||||
static final int COLUMN_INDEX_SUMMARY_OFF = 3;
|
||||
static final int COLUMN_INDEX_CLASS_NAME = 4;
|
||||
static final int COLUMN_INDEX_SCREEN_TITLE = 5;
|
||||
static final int COLUMN_INDEX_ICON = 6;
|
||||
static final int COLUMN_INDEX_INTENT_ACTION = 7;
|
||||
static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
|
||||
static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
|
||||
static final int COLUMN_INDEX_KEY = 10;
|
||||
static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
|
||||
static final int COLUMN_INDEX_PAYLOAD = 12;
|
||||
public static final int COLUMN_INDEX_ID = 0;
|
||||
public static final int COLUMN_INDEX_TITLE = 1;
|
||||
public static final int COLUMN_INDEX_SUMMARY_ON = 2;
|
||||
public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
|
||||
public static final int COLUMN_INDEX_CLASS_NAME = 4;
|
||||
public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
|
||||
public static final int COLUMN_INDEX_ICON = 6;
|
||||
public static final int COLUMN_INDEX_INTENT_ACTION = 7;
|
||||
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
|
||||
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
|
||||
public static final int COLUMN_INDEX_KEY = 10;
|
||||
public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
|
||||
public static final int COLUMN_INDEX_PAYLOAD = 12;
|
||||
|
||||
public static final String[] SELECT_COLUMNS = {
|
||||
IndexColumns.DOCID,
|
||||
|
||||
@@ -35,7 +35,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String DATABASE_NAME = "search_index.db";
|
||||
private static final int DATABASE_VERSION = 117;
|
||||
|
||||
private static final String INDEX = "index";
|
||||
private static final String SHARED_PREFS_TAG = "indexing_manager";
|
||||
|
||||
private static final String PREF_KEY_INDEXED_PROVIDERS = "indexed_providers";
|
||||
|
||||
@@ -179,7 +179,7 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
public IndexDatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,6 +230,10 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
public void reconstruct(SQLiteDatabase db) {
|
||||
mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.clear()
|
||||
.commit();
|
||||
dropTables(db);
|
||||
bootstrapDB(db);
|
||||
}
|
||||
@@ -252,24 +256,6 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a full index on an OTA or when the locale has changed
|
||||
*
|
||||
* @param locale is the default for the device
|
||||
* @param fingerprint id for the current build.
|
||||
* @return true when the locale or build has changed since last index.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isFullIndex(Context context, String locale, String fingerprint,
|
||||
String providerVersionedNames) {
|
||||
final boolean isLocaleIndexed = IndexDatabaseHelper.isLocaleAlreadyIndexed(context, locale);
|
||||
final boolean isBuildIndexed = IndexDatabaseHelper.isBuildIndexed(context, fingerprint);
|
||||
final boolean areProvidersIndexed = IndexDatabaseHelper
|
||||
.areProvidersIndexed(context, providerVersionedNames);
|
||||
|
||||
return !(isLocaleIndexed && isBuildIndexed && areProvidersIndexed);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String buildProviderVersionedNames(List<ResolveInfo> providers) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -282,44 +268,42 @@ public class IndexDatabaseHelper extends SQLiteOpenHelper {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static void clearCachedIndexed(Context context) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).edit().clear().commit();
|
||||
}
|
||||
|
||||
static void setLocaleIndexed(Context context, String locale) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
||||
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean(locale, true)
|
||||
.apply();
|
||||
}
|
||||
|
||||
static void setProvidersIndexed(Context context, String providerVersionedNames) {
|
||||
context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
||||
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putString(PREF_KEY_INDEXED_PROVIDERS, providerVersionedNames)
|
||||
.apply();
|
||||
}
|
||||
|
||||
static boolean isLocaleAlreadyIndexed(Context context, String locale) {
|
||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(locale, false);
|
||||
return context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.getBoolean(locale, false);
|
||||
}
|
||||
|
||||
static boolean areProvidersIndexed(Context context, String providerVersionedNames) {
|
||||
final String indexedProviders = context.getSharedPreferences(INDEX, Context.MODE_PRIVATE)
|
||||
.getString(PREF_KEY_INDEXED_PROVIDERS, null);
|
||||
final String indexedProviders =
|
||||
context.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE)
|
||||
.getString(PREF_KEY_INDEXED_PROVIDERS, null);
|
||||
return TextUtils.equals(indexedProviders, providerVersionedNames);
|
||||
}
|
||||
|
||||
static boolean isBuildIndexed(Context context, String buildNo) {
|
||||
return context.getSharedPreferences(INDEX, Context.MODE_PRIVATE).getBoolean(buildNo, false);
|
||||
return context.getSharedPreferences(SHARED_PREFS_TAG,
|
||||
Context.MODE_PRIVATE).getBoolean(buildNo, false);
|
||||
}
|
||||
|
||||
static void setBuildIndexed(Context context, String buildNo) {
|
||||
context.getSharedPreferences(INDEX, 0).edit().putBoolean(buildNo, true).commit();
|
||||
context.getSharedPreferences(SHARED_PREFS_TAG, 0).edit().putBoolean(buildNo, true).commit();
|
||||
}
|
||||
|
||||
private void dropTables(SQLiteDatabase db) {
|
||||
clearCachedIndexed(mContext);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_META_INDEX);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_PREFS_INDEX);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SAVED_QUERIES);
|
||||
|
||||
@@ -74,8 +74,7 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider {
|
||||
@Override
|
||||
public DatabaseIndexingManager getIndexingManager(Context context) {
|
||||
if (mDatabaseIndexingManager == null) {
|
||||
mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext(),
|
||||
context.getPackageName());
|
||||
mDatabaseIndexingManager = new DatabaseIndexingManager(context.getApplicationContext());
|
||||
}
|
||||
return mDatabaseIndexingManager;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.datausage.DataPlanUsageSummary;
|
||||
import com.android.settings.datausage.DataUsageMeteredSettings;
|
||||
import com.android.settings.datausage.DataUsageSummary;
|
||||
import com.android.settings.deletionhelper.AutomaticStorageManagerSettings;
|
||||
import com.android.settings.development.DevelopmentSettings;
|
||||
import com.android.settings.deviceinfo.StorageDashboardFragment;
|
||||
import com.android.settings.deviceinfo.StorageSettings;
|
||||
@@ -213,6 +214,10 @@ public final class SearchIndexableResources {
|
||||
R.drawable.ic_settings_notifications);
|
||||
addIndex(DreamSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_display);
|
||||
addIndex(SupportDashboardActivity.class, NO_DATA_RES_ID, R.drawable.ic_help);
|
||||
addIndex(
|
||||
AutomaticStorageManagerSettings.class,
|
||||
NO_DATA_RES_ID,
|
||||
R.drawable.ic_settings_storage);
|
||||
}
|
||||
|
||||
private SearchIndexableResources() {
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* 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.search.indexing;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.provider.SearchIndexablesContract;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settings.search.SettingsSearchIndexablesProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID;
|
||||
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_CLASS_NAME;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ENTRIES;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_ICON_RESID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_RANK;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_OFF;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SUMMARY_ON;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_USER_ID;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS;
|
||||
import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE;
|
||||
|
||||
/**
|
||||
* Collects all data from {@link android.provider.SearchIndexablesProvider} to be indexed.
|
||||
*/
|
||||
public class IndexableDataCollector {
|
||||
|
||||
private static final String TAG = "IndexableDataCollector";
|
||||
|
||||
// TODO (b/64938328) update to new search package.
|
||||
private final String BASE_AUTHORITY = "com.android.settings";
|
||||
|
||||
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private PreIndexData mIndexData;
|
||||
|
||||
public IndexableDataCollector(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public PreIndexData collectIndexableData(List<ResolveInfo> providers, boolean isFullIndex) {
|
||||
mIndexData = new PreIndexData();
|
||||
|
||||
for (final ResolveInfo info : providers) {
|
||||
if (!isWellKnownProvider(info)) {
|
||||
continue;
|
||||
}
|
||||
final String authority = info.providerInfo.authority;
|
||||
final String packageName = info.providerInfo.packageName;
|
||||
|
||||
if (isFullIndex) {
|
||||
addIndexablesFromRemoteProvider(packageName, authority);
|
||||
}
|
||||
|
||||
final long nonIndexableStartTime = System.currentTimeMillis();
|
||||
addNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||
if (SettingsSearchIndexablesProvider.DEBUG) {
|
||||
final long nonIndexableTime = System.currentTimeMillis() - nonIndexableStartTime;
|
||||
Log.d(TAG, "performIndexing update non-indexable for package " + packageName
|
||||
+ " took time: " + nonIndexableTime);
|
||||
}
|
||||
}
|
||||
|
||||
return mIndexData;
|
||||
}
|
||||
|
||||
private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
|
||||
try {
|
||||
final Context context = BASE_AUTHORITY.equals(authority) ?
|
||||
mContext : mContext.createPackageContext(packageName, 0);
|
||||
|
||||
final Uri uriForResources = buildUriForXmlResources(authority);
|
||||
mIndexData.dataToUpdate.addAll(getIndexablesForXmlResourceUri(context, packageName,
|
||||
uriForResources, SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS));
|
||||
|
||||
final Uri uriForRawData = buildUriForRawData(authority);
|
||||
mIndexData.dataToUpdate.addAll(getIndexablesForRawDataUri(context, packageName,
|
||||
uriForRawData, SearchIndexablesContract.INDEXABLES_RAW_COLUMNS));
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "Could not create context for " + packageName + ": "
|
||||
+ Log.getStackTraceString(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<SearchIndexableResource> getIndexablesForXmlResourceUri(Context packageContext,
|
||||
String packageName, Uri uri, String[] projection) {
|
||||
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
List<SearchIndexableResource> resources = new ArrayList<>();
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return resources;
|
||||
}
|
||||
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final int xmlResId = cursor.getInt(COLUMN_INDEX_XML_RES_RESID);
|
||||
|
||||
final String className = cursor.getString(COLUMN_INDEX_XML_RES_CLASS_NAME);
|
||||
final int iconResId = cursor.getInt(COLUMN_INDEX_XML_RES_ICON_RESID);
|
||||
|
||||
final String action = cursor.getString(COLUMN_INDEX_XML_RES_INTENT_ACTION);
|
||||
final String targetPackage = cursor.getString(
|
||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE);
|
||||
final String targetClass = cursor.getString(
|
||||
COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS);
|
||||
|
||||
SearchIndexableResource sir = new SearchIndexableResource(packageContext);
|
||||
sir.xmlResId = xmlResId;
|
||||
sir.className = className;
|
||||
sir.packageName = packageName;
|
||||
sir.iconResId = iconResId;
|
||||
sir.intentAction = action;
|
||||
sir.intentTargetPackage = targetPackage;
|
||||
sir.intentTargetClass = targetClass;
|
||||
|
||||
resources.add(sir);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
private void addNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||
String authority) {
|
||||
final List<String> keys =
|
||||
getNonIndexablesKeysFromRemoteProvider(packageName, authority);
|
||||
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
mIndexData.nonIndexableKeys.put(authority, new ArraySet<>(keys));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<String> getNonIndexablesKeysFromRemoteProvider(String packageName,
|
||||
String authority) {
|
||||
try {
|
||||
final Context packageContext = mContext.createPackageContext(packageName, 0);
|
||||
|
||||
final Uri uriForNonIndexableKeys = buildUriForNonIndexableKeys(authority);
|
||||
return getNonIndexablesKeys(packageContext, uriForNonIndexableKeys,
|
||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "Could not create context for " + packageName + ": "
|
||||
+ Log.getStackTraceString(e));
|
||||
return EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Uri buildUriForXmlResources(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.INDEXABLES_XML_RES_PATH);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Uri buildUriForRawData(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.INDEXABLES_RAW_PATH);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Uri buildUriForNonIndexableKeys(String authority) {
|
||||
return Uri.parse("content://" + authority + "/" +
|
||||
SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<SearchIndexableRaw> getIndexablesForRawDataUri(Context packageContext, String packageName,
|
||||
Uri uri, String[] projection) {
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
List<SearchIndexableRaw> rawData = new ArrayList<>();
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return rawData;
|
||||
}
|
||||
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final int providerRank = cursor.getInt(COLUMN_INDEX_RAW_RANK);
|
||||
// TODO Remove rank
|
||||
final String title = cursor.getString(COLUMN_INDEX_RAW_TITLE);
|
||||
final String summaryOn = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_ON);
|
||||
final String summaryOff = cursor.getString(COLUMN_INDEX_RAW_SUMMARY_OFF);
|
||||
final String entries = cursor.getString(COLUMN_INDEX_RAW_ENTRIES);
|
||||
final String keywords = cursor.getString(COLUMN_INDEX_RAW_KEYWORDS);
|
||||
|
||||
final String screenTitle = cursor.getString(COLUMN_INDEX_RAW_SCREEN_TITLE);
|
||||
|
||||
final String className = cursor.getString(COLUMN_INDEX_RAW_CLASS_NAME);
|
||||
final int iconResId = cursor.getInt(COLUMN_INDEX_RAW_ICON_RESID);
|
||||
|
||||
final String action = cursor.getString(COLUMN_INDEX_RAW_INTENT_ACTION);
|
||||
final String targetPackage = cursor.getString(
|
||||
COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE);
|
||||
final String targetClass = cursor.getString(
|
||||
COLUMN_INDEX_RAW_INTENT_TARGET_CLASS);
|
||||
|
||||
final String key = cursor.getString(COLUMN_INDEX_RAW_KEY);
|
||||
final int userId = cursor.getInt(COLUMN_INDEX_RAW_USER_ID);
|
||||
|
||||
SearchIndexableRaw data = new SearchIndexableRaw(packageContext);
|
||||
data.title = title;
|
||||
data.summaryOn = summaryOn;
|
||||
data.summaryOff = summaryOff;
|
||||
data.entries = entries;
|
||||
data.keywords = keywords;
|
||||
data.screenTitle = screenTitle;
|
||||
data.className = className;
|
||||
data.packageName = packageName;
|
||||
data.iconResId = iconResId;
|
||||
data.intentAction = action;
|
||||
data.intentTargetPackage = targetPackage;
|
||||
data.intentTargetClass = targetClass;
|
||||
data.key = key;
|
||||
data.userId = userId;
|
||||
|
||||
rawData.add(data);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
private List<String> getNonIndexablesKeys(Context packageContext, Uri uri,
|
||||
String[] projection) {
|
||||
|
||||
final ContentResolver resolver = packageContext.getContentResolver();
|
||||
final Cursor cursor = resolver.query(uri, projection, null, null, null);
|
||||
final List<String> result = new ArrayList<>();
|
||||
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
final int count = cursor.getCount();
|
||||
if (count > 0) {
|
||||
while (cursor.moveToNext()) {
|
||||
final String key = cursor.getString(COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE);
|
||||
|
||||
if (TextUtils.isEmpty(key) && Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "Empty non-indexable key from: "
|
||||
+ packageContext.getPackageName());
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow a "well known" SearchIndexablesProvider. The provider should:
|
||||
*
|
||||
* - have read/write {@link Manifest.permission#READ_SEARCH_INDEXABLES}
|
||||
* - be from a privileged package
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean isWellKnownProvider(ResolveInfo info) {
|
||||
final String authority = info.providerInfo.authority;
|
||||
final String packageName = info.providerInfo.applicationInfo.packageName;
|
||||
|
||||
if (TextUtils.isEmpty(authority) || TextUtils.isEmpty(packageName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String readPermission = info.providerInfo.readPermission;
|
||||
final String writePermission = info.providerInfo.writePermission;
|
||||
|
||||
if (TextUtils.isEmpty(readPermission) || TextUtils.isEmpty(writePermission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(readPermission) ||
|
||||
!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(writePermission)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isPrivilegedPackage(packageName, mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the {@param packageName} is privileged.
|
||||
*/
|
||||
private boolean isPrivilegedPackage(String packageName, Context context) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
PackageInfo packInfo = pm.getPackageInfo(packageName, 0);
|
||||
return ((packInfo.applicationInfo.privateFlags
|
||||
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/com/android/settings/search/indexing/PreIndexData.java
Normal file
55
src/com/android/settings/search/indexing/PreIndexData.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.search.indexing;
|
||||
|
||||
import android.provider.SearchIndexableData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* Holds Data sources for indexable data.
|
||||
* TODO (b/33577327) add getters and setters for data.
|
||||
*/
|
||||
public class PreIndexData {
|
||||
public List<SearchIndexableData> dataToUpdate;
|
||||
public Map<String, Set<String>> nonIndexableKeys;
|
||||
|
||||
public PreIndexData() {
|
||||
dataToUpdate = new ArrayList<>();
|
||||
nonIndexableKeys = new HashMap<>();
|
||||
}
|
||||
|
||||
public PreIndexData(PreIndexData other) {
|
||||
dataToUpdate = new ArrayList<>(other.dataToUpdate);
|
||||
nonIndexableKeys = new HashMap<>(other.nonIndexableKeys);
|
||||
}
|
||||
|
||||
public PreIndexData copy() {
|
||||
return new PreIndexData(this);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
dataToUpdate.clear();
|
||||
nonIndexableKeys.clear();
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class ConfigureWifiSettings extends DashboardFragment {
|
||||
final NetworkScoreManagerWrapper networkScoreManagerWrapper =
|
||||
new NetworkScoreManagerWrapper(context.getSystemService(NetworkScoreManager.class));
|
||||
mWifiWakeupPreferenceController = new WifiWakeupPreferenceController(
|
||||
context, getLifecycle(), networkScoreManagerWrapper);
|
||||
context, getLifecycle());
|
||||
mUseOpenWifiPreferenceController = new UseOpenWifiPreferenceController(context, this,
|
||||
networkScoreManagerWrapper, getLifecycle());
|
||||
final WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.net.wifi.WpsInfo;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Bundle;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
@@ -918,9 +919,8 @@ public class WifiSettings extends RestrictedSettingsFragment
|
||||
getContentResolver(), Settings.Global.WIFI_WAKEUP_AVAILABLE, defaultWakeupAvailable)
|
||||
== 1;
|
||||
if (wifiWakeupAvailable) {
|
||||
boolean wifiWakeupEnabled = Settings.Global.getInt(
|
||||
getContentResolver(), Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1;
|
||||
mConfigureWifiSettingsPreference.setSummary(getString(wifiWakeupEnabled
|
||||
mConfigureWifiSettingsPreference.setSummary(getString(
|
||||
isWifiWakeupEnabled()
|
||||
? R.string.wifi_configure_settings_preference_summary_wakeup_on
|
||||
: R.string.wifi_configure_settings_preference_summary_wakeup_off));
|
||||
}
|
||||
@@ -935,6 +935,20 @@ public class WifiSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWifiWakeupEnabled() {
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
ContentResolver contentResolver = getContentResolver();
|
||||
return Settings.Global.getInt(contentResolver,
|
||||
Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1
|
||||
&& Settings.Global.getInt(contentResolver,
|
||||
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1
|
||||
&& Settings.Global.getInt(contentResolver,
|
||||
Settings.Global.AIRPLANE_MODE_ON, 0) == 0
|
||||
&& Settings.Global.getInt(contentResolver,
|
||||
Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1
|
||||
&& !powerManager.isPowerSaveMode();
|
||||
}
|
||||
|
||||
private void setOffMessage() {
|
||||
final CharSequence title = getText(R.string.wifi_empty_list_wifi_off);
|
||||
// Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead,
|
||||
|
||||
@@ -44,13 +44,10 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause {
|
||||
|
||||
private static final String KEY_ENABLE_WIFI_WAKEUP = "enable_wifi_wakeup";
|
||||
private final NetworkScoreManagerWrapper mNetworkScoreManager;
|
||||
private SettingObserver mSettingObserver;
|
||||
|
||||
public WifiWakeupPreferenceController(
|
||||
Context context, Lifecycle lifecycle, NetworkScoreManagerWrapper networkScoreManager) {
|
||||
public WifiWakeupPreferenceController(Context context, Lifecycle lifecycle) {
|
||||
super(context);
|
||||
mNetworkScoreManager = networkScoreManager;
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
@@ -116,11 +113,9 @@ public class WifiWakeupPreferenceController extends AbstractPreferenceController
|
||||
boolean networkRecommendationsEnabled = Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1;
|
||||
boolean activeScorerSet = mNetworkScoreManager.getActiveScorerPackage() != null;
|
||||
enableWifiWakeup.setEnabled(
|
||||
networkRecommendationsEnabled && wifiScanningEnabled && activeScorerSet);
|
||||
enableWifiWakeup.setEnabled(networkRecommendationsEnabled && wifiScanningEnabled);
|
||||
|
||||
if (!activeScorerSet) {
|
||||
if (!networkRecommendationsEnabled) {
|
||||
enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary_scoring_disabled);
|
||||
} else if (!wifiScanningEnabled) {
|
||||
enableWifiWakeup.setSummary(R.string.wifi_wakeup_summary_scanning_disabled);
|
||||
|
||||
@@ -64,7 +64,6 @@ com.android.settings.notification.AppNotificationSettings
|
||||
com.android.settings.deviceinfo.PrivateVolumeSettings
|
||||
com.android.settings.users.AppRestrictionsFragment
|
||||
com.android.settings.deviceinfo.PrivateVolumeUnmount
|
||||
com.android.settings.deletionhelper.AutomaticStorageManagerSettings
|
||||
com.android.settings.notification.ZenAccessSettings
|
||||
com.android.settings.accessibility.ToggleFontSizePreferenceFragment
|
||||
com.android.settings.applications.PremiumSmsAccess
|
||||
|
||||
@@ -32,9 +32,6 @@ public class OemLockManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canUserAllowOemUnlock() {
|
||||
return true;
|
||||
}
|
||||
public boolean isOemUnlockAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -98,11 +99,29 @@ public class BluetoothDevicePreferenceTest {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedBluetoothDevice.startPairing()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(true);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider).action(
|
||||
mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
|
||||
verify(mMetricsFeatureProvider, never()).action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedBluetoothDevice.startPairing()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(false);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider).action(
|
||||
mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
|
||||
verify(mMetricsFeatureProvider).action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settings.dashboard;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.util.DiffUtil;
|
||||
import android.support.v7.util.ListUpdateCallback;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.dashboard.conditional.AirplaneModeCondition;
|
||||
@@ -35,6 +36,7 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -224,6 +226,28 @@ public class DashboardDataTest {
|
||||
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() {
|
||||
//Build testResultData
|
||||
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
|
||||
testResultData.add(new ListUpdateResult.ResultData(
|
||||
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 0, 1));
|
||||
Tile tile = new Tile();
|
||||
tile.remoteViews = mock(RemoteViews.class);
|
||||
|
||||
DashboardData prevData = new DashboardData.Builder()
|
||||
.setConditions(null)
|
||||
.setCategory(null)
|
||||
.setSuggestions(Arrays.asList(tile))
|
||||
.build();
|
||||
DashboardData currentData = new DashboardData.Builder()
|
||||
.setConditions(null)
|
||||
.setCategory(null)
|
||||
.setSuggestions(Arrays.asList(tile))
|
||||
.build();
|
||||
testDiffUtil(prevData, currentData, testResultData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when using the
|
||||
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
@@ -158,6 +159,7 @@ public class DashboardFragmentTest {
|
||||
@Test
|
||||
public void tintTileIcon_hasMetadata_shouldReturnIconTintableMetadata() {
|
||||
final Tile tile = new Tile();
|
||||
tile.icon = mock(Icon.class);
|
||||
final Bundle metaData = new Bundle();
|
||||
tile.metaData = metaData;
|
||||
|
||||
@@ -168,10 +170,19 @@ public class DashboardFragmentTest {
|
||||
assertThat(mTestFragment.tintTileIcon(tile)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tintTileIcon_noIcon_shouldReturnFalse() {
|
||||
final Tile tile = new Tile();
|
||||
final Bundle metaData = new Bundle();
|
||||
tile.metaData = metaData;
|
||||
|
||||
assertThat(mTestFragment.tintTileIcon(tile)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tintTileIcon_noMetadata_shouldReturnPackageNameCheck() {
|
||||
final Tile tile = new Tile();
|
||||
tile.icon = mock(Icon.class);
|
||||
final Intent intent = new Intent();
|
||||
tile.intent = intent;
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.android.settings.deletionhelper;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class AutomaticStorageManagerDescriptionPreferenceControllerTest {
|
||||
@Mock private PreferenceScreen mScreen;
|
||||
@Mock private Preference mPreference;
|
||||
private AutomaticStorageManagerDescriptionPreferenceController mController;
|
||||
private Context mContext = RuntimeEnvironment.application;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = new AutomaticStorageManagerDescriptionPreferenceController(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
|
||||
when(mPreference.getContext()).thenReturn(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_asmDisabled_shouldHaveDescription() {
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setSummary(eq(R.string.automatic_storage_manager_text));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_asmEnabledButUnused_shouldHaveDescription() {
|
||||
Settings.Secure.putInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
|
||||
1);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setSummary(eq(R.string.automatic_storage_manager_text));
|
||||
}
|
||||
|
||||
@Ignore("Robolectric doesn't do locale switching for date localization -- yet.")
|
||||
@Test
|
||||
@Config(qualifiers = "en")
|
||||
public void displayPreference_asmEnabledAndUsed_shouldHaveDescriptionFilledOut() {
|
||||
Settings.Secure.putInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
|
||||
1);
|
||||
Settings.Secure.putLong(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
|
||||
10);
|
||||
Settings.Secure.putLong(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
|
||||
43200000); // January 1, 1970 12:00:00 PM to avoid timezone issues.
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference)
|
||||
.setSummary(eq("10.00B total made available\n\nLast ran on January 1, 1970"));
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,18 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
|
||||
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
|
||||
import com.android.settingslib.drawer.CategoryKey;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -68,6 +73,47 @@ public class StorageDashboardFragmentTest {
|
||||
verify(activity).invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_cacheProviderProvidesValuesIfBothCached() {
|
||||
CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
|
||||
PrivateStorageInfo info = new PrivateStorageInfo(0, 0);
|
||||
when(helper.getCachedPrivateStorageInfo()).thenReturn(info);
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
|
||||
when(helper.getCachedAppsStorageResult()).thenReturn(result);
|
||||
|
||||
mFragment.setCachedStorageValuesHelper(helper);
|
||||
mFragment.initializeCachedValues();
|
||||
|
||||
assertThat(mFragment.getPrivateStorageInfo()).isEqualTo(info);
|
||||
assertThat(mFragment.getAppsStorageResult()).isEqualTo(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_cacheProviderDoesntProvideValuesIfAppsMissing() {
|
||||
CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
|
||||
PrivateStorageInfo info = new PrivateStorageInfo(0, 0);
|
||||
when(helper.getCachedPrivateStorageInfo()).thenReturn(info);
|
||||
|
||||
mFragment.setCachedStorageValuesHelper(helper);
|
||||
mFragment.initializeCachedValues();
|
||||
|
||||
assertThat(mFragment.getPrivateStorageInfo()).isNull();
|
||||
assertThat(mFragment.getAppsStorageResult()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_cacheProviderDoesntProvideValuesIfVolumeInfoMissing() {
|
||||
CachedStorageValuesHelper helper = mock(CachedStorageValuesHelper.class);
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
|
||||
when(helper.getCachedAppsStorageResult()).thenReturn(result);
|
||||
|
||||
mFragment.setCachedStorageValuesHelper(helper);
|
||||
mFragment.initializeCachedValues();
|
||||
|
||||
assertThat(mFragment.getPrivateStorageInfo()).isNull();
|
||||
assertThat(mFragment.getAppsStorageResult()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchIndexProvider_shouldIndexResource() {
|
||||
final List<SearchIndexableResource> indexRes =
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.deviceinfo.storage;
|
||||
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.CACHE_APPS_SIZE_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_APP_BYTES;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_AUDIO_BYTES;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_IMAGE_BYTES;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_TOTAL_BYTES;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.EXTERNAL_VIDEO_BYTES;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.FREE_BYTES_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.GAME_APPS_SIZE_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.MUSIC_APPS_SIZE_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.OTHER_APPS_SIZE_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.PHOTO_APPS_SIZE_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.SHARED_PREFERENCES_NAME;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TIMESTAMP_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.TOTAL_BYTES_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.USER_ID_KEY;
|
||||
import static com.android.settings.deviceinfo.storage.CachedStorageValuesHelper.VIDEO_APPS_SIZE_KEY;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settingslib.applications.StorageStatsSource;
|
||||
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class CachedStorageValuesHelperTest {
|
||||
private Context mContext;
|
||||
|
||||
@Mock private CachedStorageValuesHelper.Clock mMockClock;
|
||||
private CachedStorageValuesHelper mCachedValuesHelper;
|
||||
private SharedPreferences mSharedPreferences;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application.getApplicationContext();
|
||||
mSharedPreferences = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, 0);
|
||||
mCachedValuesHelper = new CachedStorageValuesHelper(mContext, 0);
|
||||
mCachedValuesHelper.mClock = mMockClock;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedPrivateStorageInfo_cachedValuesAreLoaded() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10001L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 0)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 2)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 6000L)
|
||||
.putInt(USER_ID_KEY, 0)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
|
||||
|
||||
assertThat(info.freeBytes).isEqualTo(1000L);
|
||||
assertThat(info.totalBytes).isEqualTo(6000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedAppsStorageResult_cachedValuesAreLoaded() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10001L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 1)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 222222)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 5000L)
|
||||
.putInt(USER_ID_KEY, 0)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result =
|
||||
mCachedValuesHelper.getCachedAppsStorageResult();
|
||||
|
||||
StorageAsyncLoader.AppsStorageResult primaryResult = result.get(0);
|
||||
assertThat(primaryResult.gamesSize).isEqualTo(1L);
|
||||
assertThat(primaryResult.musicAppsSize).isEqualTo(10L);
|
||||
assertThat(primaryResult.videoAppsSize).isEqualTo(100L);
|
||||
assertThat(primaryResult.photosAppsSize).isEqualTo(1000L);
|
||||
assertThat(primaryResult.otherAppsSize).isEqualTo(10000L);
|
||||
assertThat(primaryResult.cacheSize).isEqualTo(100000L);
|
||||
assertThat(primaryResult.externalStats.totalBytes).isEqualTo(222222L);
|
||||
assertThat(primaryResult.externalStats.audioBytes).isEqualTo(22L);
|
||||
assertThat(primaryResult.externalStats.videoBytes).isEqualTo(222L);
|
||||
assertThat(primaryResult.externalStats.imageBytes).isEqualTo(2222L);
|
||||
assertThat(primaryResult.externalStats.appBytes).isEqualTo(22222L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedPrivateStorageInfo_nullIfDataIsStale() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10000000L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 0)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 2)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 5000L)
|
||||
.putInt(USER_ID_KEY, 0)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
|
||||
assertThat(info).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedAppsStorageResult_nullIfDataIsStale() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10000000L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 0)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 2)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 5000L)
|
||||
.putInt(USER_ID_KEY, 0)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result =
|
||||
mCachedValuesHelper.getCachedAppsStorageResult();
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedPrivateStorageInfo_nullIfWrongUser() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10001L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 0)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 2)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 5000L)
|
||||
.putInt(USER_ID_KEY, 1)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
|
||||
assertThat(info).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedAppsStorageResult_nullIfWrongUser() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10001L);
|
||||
mSharedPreferences
|
||||
.edit()
|
||||
.putLong(GAME_APPS_SIZE_KEY, 0)
|
||||
.putLong(MUSIC_APPS_SIZE_KEY, 10)
|
||||
.putLong(VIDEO_APPS_SIZE_KEY, 100)
|
||||
.putLong(PHOTO_APPS_SIZE_KEY, 1000)
|
||||
.putLong(OTHER_APPS_SIZE_KEY, 10000)
|
||||
.putLong(CACHE_APPS_SIZE_KEY, 100000)
|
||||
.putLong(EXTERNAL_TOTAL_BYTES, 2)
|
||||
.putLong(EXTERNAL_AUDIO_BYTES, 22)
|
||||
.putLong(EXTERNAL_VIDEO_BYTES, 222)
|
||||
.putLong(EXTERNAL_IMAGE_BYTES, 2222)
|
||||
.putLong(EXTERNAL_APP_BYTES, 22222)
|
||||
.putLong(FREE_BYTES_KEY, 1000L)
|
||||
.putLong(TOTAL_BYTES_KEY, 5000L)
|
||||
.putInt(USER_ID_KEY, 1)
|
||||
.putLong(TIMESTAMP_KEY, 10000L)
|
||||
.apply();
|
||||
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result =
|
||||
mCachedValuesHelper.getCachedAppsStorageResult();
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedPrivateStorageInfo_nullIfEmpty() throws Exception {
|
||||
PrivateStorageInfo info = mCachedValuesHelper.getCachedPrivateStorageInfo();
|
||||
assertThat(info).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCachedAppsStorageResult_nullIfEmpty() throws Exception {
|
||||
SparseArray<StorageAsyncLoader.AppsStorageResult> result =
|
||||
mCachedValuesHelper.getCachedAppsStorageResult();
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheResult_succeeds() throws Exception {
|
||||
when(mMockClock.getCurrentTime()).thenReturn(10000L);
|
||||
final StorageStatsSource.ExternalStorageStats externalStats =
|
||||
new StorageStatsSource.ExternalStorageStats(22222l, 2l, 20L, 200L, 2000L);
|
||||
final StorageAsyncLoader.AppsStorageResult result =
|
||||
new StorageAsyncLoader.AppsStorageResult();
|
||||
result.gamesSize = 1L;
|
||||
result.musicAppsSize = 10l;
|
||||
result.videoAppsSize = 100L;
|
||||
result.photosAppsSize = 1000L;
|
||||
result.otherAppsSize = 10000L;
|
||||
result.cacheSize = 100000l;
|
||||
result.externalStats = externalStats;
|
||||
final PrivateStorageInfo info = new PrivateStorageInfo(1000L, 6000L);
|
||||
|
||||
mCachedValuesHelper.cacheResult(info, result);
|
||||
|
||||
assertThat(mSharedPreferences.getLong(GAME_APPS_SIZE_KEY, -1)).isEqualTo(1L);
|
||||
assertThat(mSharedPreferences.getLong(MUSIC_APPS_SIZE_KEY, -1)).isEqualTo(10L);
|
||||
assertThat(mSharedPreferences.getLong(VIDEO_APPS_SIZE_KEY, -1)).isEqualTo(100L);
|
||||
assertThat(mSharedPreferences.getLong(PHOTO_APPS_SIZE_KEY, -1)).isEqualTo(1000L);
|
||||
assertThat(mSharedPreferences.getLong(OTHER_APPS_SIZE_KEY, -1)).isEqualTo(10000L);
|
||||
assertThat(mSharedPreferences.getLong(CACHE_APPS_SIZE_KEY, -1)).isEqualTo(100000L);
|
||||
assertThat(mSharedPreferences.getLong(EXTERNAL_TOTAL_BYTES, -1)).isEqualTo(22222L);
|
||||
assertThat(mSharedPreferences.getLong(EXTERNAL_AUDIO_BYTES, -1)).isEqualTo(2L);
|
||||
assertThat(mSharedPreferences.getLong(EXTERNAL_VIDEO_BYTES, -1)).isEqualTo(20L);
|
||||
assertThat(mSharedPreferences.getLong(EXTERNAL_IMAGE_BYTES, -1)).isEqualTo(200L);
|
||||
assertThat(mSharedPreferences.getLong(EXTERNAL_APP_BYTES, -1)).isEqualTo(2000L);
|
||||
assertThat(mSharedPreferences.getLong(FREE_BYTES_KEY, -1)).isEqualTo(1000L);
|
||||
assertThat(mSharedPreferences.getLong(TOTAL_BYTES_KEY, -1)).isEqualTo(6000L);
|
||||
assertThat(mSharedPreferences.getInt(USER_ID_KEY, -1)).isEqualTo(0);
|
||||
assertThat(mSharedPreferences.getLong(TIMESTAMP_KEY, -1)).isEqualTo(10000L);
|
||||
};
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
@@ -43,7 +44,7 @@ import java.util.List;
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class AssistGestureSettingsTest {
|
||||
@Mock
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
private FakeFeatureFactory mFakeFeatureFactory;
|
||||
private AssistGestureSettings mSettings;
|
||||
@@ -51,6 +52,7 @@ public class AssistGestureSettingsTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
FakeFeatureFactory.setupForTest(mContext);
|
||||
mFakeFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
|
||||
mSettings = new AssistGestureSettings();
|
||||
}
|
||||
|
||||
@@ -17,24 +17,20 @@
|
||||
|
||||
package com.android.settings.search;
|
||||
|
||||
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyList;
|
||||
import static org.mockito.Matchers.anyMap;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -43,19 +39,18 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.SearchIndexableData;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.search.indexing.PreIndexData;
|
||||
import com.android.settings.testutils.DatabaseTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowRunnableAsyncTask;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -67,7 +62,6 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowContentResolver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -84,8 +78,6 @@ import java.util.Set;
|
||||
sdk = TestConfig.SDK_VERSION,
|
||||
shadows = {
|
||||
ShadowRunnableAsyncTask.class,
|
||||
ShadowDatabaseIndexingUtils.class,
|
||||
ShadowContentResolver.class
|
||||
}
|
||||
)
|
||||
public class DatabaseIndexingManagerTest {
|
||||
@@ -129,6 +121,8 @@ public class DatabaseIndexingManagerTest {
|
||||
private DatabaseIndexingManager mManager;
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
private final List<ResolveInfo> FAKE_PROVIDER_LIST = new ArrayList<>();
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
@@ -136,10 +130,12 @@ public class DatabaseIndexingManagerTest {
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mManager = spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
mManager = spy(new DatabaseIndexingManager(mContext));
|
||||
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
|
||||
|
||||
doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
doReturn(FAKE_PROVIDER_LIST).when(mPackageManager)
|
||||
.queryIntentContentProviders(any(Intent.class), anyInt());
|
||||
FakeFeatureFactory.setupForTest(mContext);
|
||||
}
|
||||
|
||||
@@ -755,113 +751,60 @@ public class DatabaseIndexingManagerTest {
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_fullIndex_getsDataFromProviders() {
|
||||
DummyProvider provider = new DummyProvider();
|
||||
provider.onCreate();
|
||||
ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
|
||||
SearchIndexableRaw rawData = getFakeRaw();
|
||||
PreIndexData data = getPreIndexData(rawData);
|
||||
doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean());
|
||||
doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||
anyString());
|
||||
|
||||
// Test that Indexables are added for Full indexing
|
||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||
.thenReturn(getDummyResolveInfo());
|
||||
mManager.performIndexing();
|
||||
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
verify(manager).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
|
||||
verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
|
||||
verify(mManager).updateDatabase(data, true /* isFullIndex */,
|
||||
Locale.getDefault().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_incrementalIndex_noDataAdded() {
|
||||
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||
skipFullIndex(providerInfo);
|
||||
DummyProvider provider = new DummyProvider();
|
||||
provider.onCreate();
|
||||
ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
|
||||
// Test that Indexables are added for Full indexing
|
||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||
.thenReturn(providerInfo);
|
||||
|
||||
public void testPerformIndexing_fullIndex_databaseDropped() {
|
||||
// Initialize the Manager and force rebuild
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
|
||||
manager.mDataToProcess.dataToUpdate.clear();
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
verify(manager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
||||
anyList(), anyMap());
|
||||
verify(manager, times(0)).addIndexablesFromRemoteProvider(PACKAGE_ONE, AUTHORITY_ONE);
|
||||
verify(manager).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_localeChanged_databaseDropped() {
|
||||
DummyProvider provider = new DummyProvider();
|
||||
provider.onCreate();
|
||||
ShadowContentResolver.registerProvider(AUTHORITY_ONE, provider);
|
||||
|
||||
// Test that Indexables are added for Full indexing
|
||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||
.thenReturn(getDummyResolveInfo());
|
||||
|
||||
// Initialize the Manager
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
spy(new DatabaseIndexingManager(mContext));
|
||||
doReturn(false).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||
anyString());
|
||||
|
||||
// Insert data point which will be dropped
|
||||
final String oldTitle = "This is French";
|
||||
insertSpecialCase(oldTitle, true, "key");
|
||||
|
||||
// Add a data point to be added by the indexing
|
||||
SearchIndexableRaw raw = new SearchIndexableRaw(mContext);
|
||||
final String newTitle = "This is English";
|
||||
raw.title = newTitle;
|
||||
manager.mDataToProcess.dataToUpdate.add(raw);
|
||||
insertSpecialCase("Ceci n'est pas un pipe", true, "oui oui mon ami");
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
// Assert that the New Title is inserted
|
||||
final Cursor newCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
|
||||
newTitle + "'", null);
|
||||
assertThat(newCursor.getCount()).isEqualTo(1);
|
||||
|
||||
// Assert that the Old Title is no longer in the database, since it was dropped
|
||||
final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE data_title = '" +
|
||||
oldTitle + "'", null);
|
||||
final Cursor oldCursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||
|
||||
assertThat(oldCursor.getCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_onOta_FullIndex() {
|
||||
DummyProvider provider = new DummyProvider();
|
||||
provider.onCreate();
|
||||
ShadowContentResolver.registerProvider(
|
||||
AUTHORITY_ONE, provider
|
||||
);
|
||||
public void testPerformIndexing_isfullIndex() {
|
||||
SearchIndexableRaw rawData = getFakeRaw();
|
||||
PreIndexData data = getPreIndexData(rawData);
|
||||
doReturn(data).when(mManager).getIndexDataFromProviders(anyList(), anyBoolean());
|
||||
doReturn(true).when(mManager).isFullIndex(any(Context.class), anyString(), anyString(),
|
||||
anyString());
|
||||
|
||||
// Test that Indexables are added for Full indexing
|
||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||
.thenReturn(getDummyResolveInfo());
|
||||
mManager.performIndexing();
|
||||
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
verify(manager).updateDatabase(true /* isFullIndex */, Locale.getDefault().toString());
|
||||
verify(mManager).updateDatabase(data, true /* isFullIndex */,
|
||||
Locale.getDefault().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_onPackageChange_shouldFullIndex() {
|
||||
public void testPerformIndexing_onPackageChange_fullIndex() {
|
||||
final List<ResolveInfo> providers = getDummyResolveInfo();
|
||||
final String buildNumber = Build.FINGERPRINT;
|
||||
final String locale = Locale.getDefault().toString();
|
||||
skipFullIndex(providers);
|
||||
|
||||
// This snapshot is already indexed. Should return false
|
||||
assertThat(IndexDatabaseHelper.isFullIndex(
|
||||
assertThat(mManager.isFullIndex(
|
||||
mContext, locale, buildNumber,
|
||||
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
||||
.isFalse();
|
||||
@@ -869,65 +812,46 @@ public class DatabaseIndexingManagerTest {
|
||||
// Change provider version number, this should trigger full index.
|
||||
providers.get(0).providerInfo.applicationInfo.versionCode++;
|
||||
|
||||
assertThat(IndexDatabaseHelper.isFullIndex(mContext, locale, buildNumber,
|
||||
assertThat(mManager.isFullIndex(mContext, locale, buildNumber,
|
||||
IndexDatabaseHelper.buildProviderVersionedNames(providers)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformIndexing_onOta_buildNumberIsCached() {
|
||||
DummyProvider provider = new DummyProvider();
|
||||
provider.onCreate();
|
||||
ShadowContentResolver.registerProvider(
|
||||
AUTHORITY_ONE, provider
|
||||
);
|
||||
mManager.performIndexing();
|
||||
|
||||
// Test that Indexables are added for Full indexing
|
||||
when(mPackageManager.queryIntentContentProviders(any(Intent.class), anyInt()))
|
||||
.thenReturn(getDummyResolveInfo());
|
||||
|
||||
DatabaseIndexingManager manager =
|
||||
spy(new DatabaseIndexingManager(mContext, PACKAGE_ONE));
|
||||
|
||||
manager.performIndexing();
|
||||
|
||||
assertThat(IndexDatabaseHelper.getInstance(mContext).isBuildIndexed(mContext,
|
||||
Build.FINGERPRINT)).isTrue();
|
||||
assertThat(IndexDatabaseHelper.isBuildIndexed(mContext, Build.FINGERPRINT)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullUpdatedDatabase_noData_addDataToDatabaseNotCalled() {
|
||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
||||
mManager.mDataToProcess.dataToUpdate.clear();
|
||||
PreIndexData emptydata = new PreIndexData();
|
||||
mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr);
|
||||
verify(mManager, times(0)).addDataToDatabase(any(SQLiteDatabase.class), anyString(),
|
||||
anyList(), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFullUpdatedDatabase_updatedDataInDatabaseNotCalled() {
|
||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
||||
verify(mManager, times(0)).updateDataInDatabase(any(SQLiteDatabase.class), anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocaleUpdated_afterIndexing_localeNotAdded() {
|
||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
||||
assertThat(IndexDatabaseHelper.getInstance(mContext)
|
||||
.isLocaleAlreadyIndexed(mContext, localeStr)).isFalse();
|
||||
PreIndexData emptydata = new PreIndexData();
|
||||
mManager.updateDatabase(emptydata, true /* isFullIndex */, localeStr);
|
||||
|
||||
assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocaleUpdated_afterFullIndexing_localeAdded() {
|
||||
mManager.performIndexing();
|
||||
assertThat(IndexDatabaseHelper.getInstance(mContext)
|
||||
.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
|
||||
assertThat(IndexDatabaseHelper.isLocaleAlreadyIndexed(mContext, localeStr)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDatabase_newEligibleData_addedToDatabase() {
|
||||
// Test that addDataToDatabase is called when dataToUpdate is non-empty
|
||||
mManager.mDataToProcess.dataToUpdate.add(getFakeRaw());
|
||||
mManager.updateDatabase(true /* isFullIndex */, localeStr);
|
||||
PreIndexData indexData = new PreIndexData();
|
||||
indexData.dataToUpdate.add(getFakeRaw());
|
||||
mManager.updateDatabase(indexData, true /* isFullIndex */, localeStr);
|
||||
|
||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
|
||||
cursor.moveToPosition(0);
|
||||
@@ -1020,8 +944,8 @@ public class DatabaseIndexingManagerTest {
|
||||
@Test
|
||||
public void testEmptyNonIndexableKeys_emptyDataKeyResources_addedToDatabase() {
|
||||
insertSpecialCase(TITLE_ONE, true /* enabled */, null /* dataReferenceKey */);
|
||||
|
||||
mManager.updateDatabase(false, localeStr);
|
||||
PreIndexData emptydata = new PreIndexData();
|
||||
mManager.updateDatabase(emptydata, false /* needsReindexing */, localeStr);
|
||||
|
||||
Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
|
||||
cursor.moveToPosition(0);
|
||||
@@ -1111,46 +1035,6 @@ public class DatabaseIndexingManagerTest {
|
||||
return niks;
|
||||
}
|
||||
|
||||
private List<ResolveInfo> getDummyResolveInfo() {
|
||||
List<ResolveInfo> infoList = new ArrayList<>();
|
||||
ResolveInfo info = new ResolveInfo();
|
||||
info.providerInfo = new ProviderInfo();
|
||||
info.providerInfo.exported = true;
|
||||
info.providerInfo.authority = AUTHORITY_ONE;
|
||||
info.providerInfo.packageName = PACKAGE_ONE;
|
||||
info.providerInfo.applicationInfo = new ApplicationInfo();
|
||||
infoList.add(info);
|
||||
|
||||
return infoList;
|
||||
}
|
||||
|
||||
// TODO move this method and its counterpart in CursorToSearchResultConverterTest into
|
||||
// a util class with public fields to assert values.
|
||||
private Cursor getDummyCursor() {
|
||||
MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS);
|
||||
final String BLANK = "";
|
||||
|
||||
ArrayList<String> item =
|
||||
new ArrayList<>(INDEXABLES_RAW_COLUMNS.length);
|
||||
item.add("42"); // Rank
|
||||
item.add(TITLE_ONE); // Title
|
||||
item.add(BLANK); // Summary on
|
||||
item.add(BLANK); // summary off
|
||||
item.add(BLANK); // entries
|
||||
item.add(BLANK); // keywords
|
||||
item.add(BLANK); // screen title
|
||||
item.add(BLANK); // classname
|
||||
item.add("123"); // Icon
|
||||
item.add(BLANK); // Intent action
|
||||
item.add(BLANK); // target package
|
||||
item.add(BLANK); // target class
|
||||
item.add(KEY_ONE); // Key
|
||||
item.add("-1"); // userId
|
||||
cursor.addRow(item);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private void insertSpecialCase(String specialCase, boolean enabled, String key) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(IndexDatabaseHelper.IndexColumns.DOCID, specialCase.hashCode());
|
||||
@@ -1179,43 +1063,22 @@ public class DatabaseIndexingManagerTest {
|
||||
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
|
||||
}
|
||||
|
||||
private class DummyProvider extends ContentProvider {
|
||||
private PreIndexData getPreIndexData(SearchIndexableData fakeData) {
|
||||
PreIndexData data = new PreIndexData();
|
||||
data.dataToUpdate.add(fakeData);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
private List<ResolveInfo> getDummyResolveInfo() {
|
||||
List<ResolveInfo> infoList = new ArrayList<>();
|
||||
ResolveInfo info = new ResolveInfo();
|
||||
info.providerInfo = new ProviderInfo();
|
||||
info.providerInfo.exported = true;
|
||||
info.providerInfo.authority = AUTHORITY_ONE;
|
||||
info.providerInfo.packageName = PACKAGE_ONE;
|
||||
info.providerInfo.applicationInfo = new ApplicationInfo();
|
||||
infoList.add(info);
|
||||
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
|
||||
@Nullable String selection, @Nullable String[] selectionArgs,
|
||||
@Nullable String sortOrder) {
|
||||
if (uri.toString().contains("xml")) {
|
||||
return null;
|
||||
}
|
||||
return getDummyCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, @Nullable String selection,
|
||||
@Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues values,
|
||||
@Nullable String selection, @Nullable String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
return infoList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.search.indexing;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class IndexableDataCollectorTest {
|
||||
|
||||
private final String AUTHORITY_ONE = "authority";
|
||||
private final String PACKAGE_ONE = "com.android.settings";
|
||||
|
||||
@Mock
|
||||
ContentResolver mResolver;
|
||||
|
||||
Context mContext;
|
||||
|
||||
IndexableDataCollector mDataCollector;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(mResolver).when(mContext).getContentResolver();
|
||||
//doReturn(mPackageManager).when(mContext).getPackageManager();
|
||||
|
||||
mDataCollector = spy(new IndexableDataCollector(mContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectIndexableData_addsResourceData() {
|
||||
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||
|
||||
List<SearchIndexableResource> resources = getFakeResource();
|
||||
doReturn(resources).when(mDataCollector).getIndexablesForXmlResourceUri(
|
||||
any(Context.class), anyString(), any(Uri.class), any(String[].class));
|
||||
|
||||
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||
true /* isFullIndex */);
|
||||
|
||||
assertThat(data.dataToUpdate).containsAllIn(resources);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectIndexableData_addsRawData() {
|
||||
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||
|
||||
List<SearchIndexableRaw> rawData = getFakeRaw();
|
||||
doReturn(rawData).when(mDataCollector).getIndexablesForRawDataUri(any(Context.class),
|
||||
anyString(), any(Uri.class), any(String[].class));
|
||||
|
||||
|
||||
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||
true /* isFullIndex */);
|
||||
|
||||
assertThat(data.dataToUpdate).containsAllIn(rawData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectIndexableData_addsNonIndexables() {
|
||||
final List<ResolveInfo> providerInfo = getDummyResolveInfo();
|
||||
doReturn(true).when(mDataCollector).isWellKnownProvider(any(ResolveInfo.class));
|
||||
|
||||
List<String> niks = getFakeNonIndexables();
|
||||
|
||||
doReturn(niks).when(mDataCollector).getNonIndexablesKeysFromRemoteProvider(anyString(),
|
||||
anyString());
|
||||
|
||||
PreIndexData data = mDataCollector.collectIndexableData(providerInfo,
|
||||
true /* isFullIndex */);
|
||||
|
||||
assertThat(data.nonIndexableKeys.get(AUTHORITY_ONE)).containsAllIn(niks);
|
||||
}
|
||||
|
||||
private List<ResolveInfo> getDummyResolveInfo() {
|
||||
List<ResolveInfo> infoList = new ArrayList<>();
|
||||
ResolveInfo info = new ResolveInfo();
|
||||
info.providerInfo = new ProviderInfo();
|
||||
info.providerInfo.exported = true;
|
||||
info.providerInfo.authority = AUTHORITY_ONE;
|
||||
info.providerInfo.packageName = PACKAGE_ONE;
|
||||
info.providerInfo.applicationInfo = new ApplicationInfo();
|
||||
infoList.add(info);
|
||||
|
||||
return infoList;
|
||||
}
|
||||
|
||||
private List<SearchIndexableResource> getFakeResource() {
|
||||
List<SearchIndexableResource> resources = new ArrayList<>();
|
||||
final String BLANK = "";
|
||||
|
||||
SearchIndexableResource sir = new SearchIndexableResource(mContext);
|
||||
sir.rank = 0;
|
||||
sir.xmlResId = 0;
|
||||
sir.className = BLANK;
|
||||
sir.packageName = BLANK;
|
||||
sir.iconResId = 0;
|
||||
sir.intentAction = BLANK;
|
||||
sir.intentTargetPackage = BLANK;
|
||||
sir.intentTargetClass = BLANK;
|
||||
sir.enabled = true;
|
||||
resources.add(sir);
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
private List<SearchIndexableRaw> getFakeRaw() {
|
||||
List<SearchIndexableRaw> rawData = new ArrayList<>();
|
||||
|
||||
SearchIndexableRaw data = new SearchIndexableRaw(mContext);
|
||||
data.title = "bront";
|
||||
data.key = "brint";
|
||||
rawData.add(data);
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
private List<String> getFakeNonIndexables() {
|
||||
List<String> niks = new ArrayList<>();
|
||||
niks.add("they're");
|
||||
niks.add("good");
|
||||
niks.add("dogs");
|
||||
niks.add("brent");
|
||||
return niks;
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,6 @@ public class WifiWakeupPreferenceControllerTest {
|
||||
private static final String TEST_SCORER_PACKAGE_NAME = "Test Scorer";
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private NetworkScoreManagerWrapper mNetworkScorer;
|
||||
private WifiWakeupPreferenceController mController;
|
||||
|
||||
@Before
|
||||
@@ -65,11 +63,11 @@ public class WifiWakeupPreferenceControllerTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new WifiWakeupPreferenceController(
|
||||
mContext, mock(Lifecycle.class), mNetworkScorer);
|
||||
mContext, mock(Lifecycle.class));
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
|
||||
SettingsShadowResources.overrideResource(
|
||||
com.android.internal.R.integer.config_wifi_wakeup_available, 0);
|
||||
when(mNetworkScorer.getActiveScorerPackage()).thenReturn(TEST_SCORER_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -116,9 +114,8 @@ public class WifiWakeupPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_preferenceSetCheckedAndSetEnabledWhenSettingsAreEnabled() {
|
||||
public void updateState_preferenceSetCheckedAndSetEnabledWhenWakeupSettingEnabled() {
|
||||
final SwitchPreference preference = mock(SwitchPreference.class);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1);
|
||||
|
||||
mController.updateState(preference);
|
||||
@@ -129,22 +126,20 @@ public class WifiWakeupPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_preferenceSetCheckedAndSetEnabledWhenSettingsAreDisabled() {
|
||||
public void updateState_preferenceSetUncheckedAndSetEnabledWhenWakeupSettingDisabled() {
|
||||
final SwitchPreference preference = mock(SwitchPreference.class);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 0);
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 0);
|
||||
|
||||
mController.updateState(preference);
|
||||
|
||||
verify(preference).setChecked(false);
|
||||
verify(preference).setEnabled(false);
|
||||
verify(preference).setEnabled(true);
|
||||
verify(preference).setSummary(R.string.wifi_wakeup_summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_preferenceSetUncheckedAndSetDisabledWhenWifiScanningDisabled() {
|
||||
final SwitchPreference preference = mock(SwitchPreference.class);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1);
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
|
||||
@@ -158,9 +153,8 @@ public class WifiWakeupPreferenceControllerTest {
|
||||
@Test
|
||||
public void updateState_preferenceSetUncheckedAndSetDisabledWhenScoringDisabled() {
|
||||
final SwitchPreference preference = mock(SwitchPreference.class);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 1);
|
||||
Settings.System.putInt(mContext.getContentResolver(), WIFI_WAKEUP_ENABLED, 1);
|
||||
when(mNetworkScorer.getActiveScorerPackage()).thenReturn(null);
|
||||
Settings.System.putInt(mContext.getContentResolver(), NETWORK_RECOMMENDATIONS_ENABLED, 0);
|
||||
|
||||
mController.updateState(preference);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user