/* * Copyright (C) 2016 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.dialer.shortcuts; import android.Manifest; import android.annotation.TargetApi; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.database.Cursor; import android.net.Uri; import android.os.Build.VERSION_CODES; import android.provider.ContactsContract.Contacts; import android.support.annotation.NonNull; import android.support.annotation.WorkerThread; import android.support.v4.content.ContextCompat; import android.util.ArrayMap; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Handles refreshing of dialer pinned shortcuts. * *
Pinned shortcuts are icons that the user has dragged to their home screen from the dialer * application launcher shortcut menu, which is accessible by tapping and holding the dialer * launcher icon from the app drawer or a home screen. * *
When refreshing pinned shortcuts, we check to make sure that pinned contact information is
* still up to date (e.g. photo and name). We also check to see if the contact has been deleted from
* the user's contacts, and if so, we disable the pinned shortcut.
*/
@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
final class PinnedShortcuts {
private static final String[] PROJECTION =
new String[] {
Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
};
private static class Delta {
final List If the delta is non-empty, it is applied by making appropriate calls to the {@link
* ShortcutManager} system service.
*
* This is a slow blocking call which performs file I/O and should not be performed on the main
* thread.
*/
@WorkerThread
public void refresh() {
Assert.isWorkerThread();
LogUtil.enterBlock("PinnedShortcuts.refresh");
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
LogUtil.i("PinnedShortcuts.refresh", "no contact permissions");
return;
}
Delta delta = new Delta();
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
for (ShortcutInfo shortcutInfo : shortcutManager.getPinnedShortcuts()) {
if (shortcutInfo.isDeclaredInManifest()) {
// We never update/disable the manifest shortcut (the "create new contact" shortcut).
continue;
}
if (shortcutInfo.isDynamic()) {
// If the shortcut is both pinned and dynamic, let the logic which updates dynamic shortcuts
// handle the update. It would be problematic to try and apply the update here, because the
// setRank is nonsensical for pinned shortcuts and therefore could not be calculated.
continue;
}
// Exclude shortcuts not for contacts.
String action = null;
if (shortcutInfo.getIntent() != null) {
action = shortcutInfo.getIntent().getAction();
}
if (action == null || !action.equals("com.android.dialer.shortcuts.CALL_CONTACT")) {
continue;
}
String lookupKey = DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo);
Uri lookupUri = DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo);
try (Cursor cursor =
context.getContentResolver().query(lookupUri, PROJECTION, null, null, null)) {
if (cursor == null || !cursor.moveToNext()) {
LogUtil.i("PinnedShortcuts.refresh", "contact disabled");
delta.shortcutIdsToDisable.add(shortcutInfo.getId());
continue;
}
// Note: The lookup key may have changed but we cannot refresh it because that would require
// changing the shortcut ID, which can only be accomplished with a remove and add; but
// pinned shortcuts cannot be added or removed.
DialerShortcut shortcut =
DialerShortcut.builder()
.setContactId(cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)))
.setLookupKey(lookupKey)
.setDisplayName(
cursor.getString(cursor.getColumnIndexOrThrow(Contacts.DISPLAY_NAME_PRIMARY)))
.build();
if (shortcut.needsUpdate(shortcutInfo)) {
LogUtil.i("PinnedShortcuts.refresh", "contact updated");
delta.shortcutsToUpdateById.put(shortcutInfo.getId(), shortcut);
}
}
}
applyDelta(delta);
}
private void applyDelta(@NonNull Delta delta) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
String shortcutDisabledMessage =
context.getResources().getString(R.string.dialer_shortcut_disabled_message);
if (!delta.shortcutIdsToDisable.isEmpty()) {
shortcutManager.disableShortcuts(delta.shortcutIdsToDisable, shortcutDisabledMessage);
}
if (!delta.shortcutsToUpdateById.isEmpty()) {
// Note: This call updates both pinned and dynamic shortcuts, but the delta should contain
// no dynamic shortcuts.
if (!shortcutManager.updateShortcuts(
shortcutInfoFactory.buildShortcutInfos(delta.shortcutsToUpdateById))) {
LogUtil.i("PinnedShortcuts.applyDelta", "shortcutManager rate limited.");
}
}
}
}