1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dialer.shortcuts;
18 
19 import android.annotation.TargetApi;
20 import android.content.pm.ShortcutInfo;
21 import android.net.Uri;
22 import android.os.Build.VERSION_CODES;
23 import android.provider.ContactsContract.Contacts;
24 import android.support.annotation.NonNull;
25 import com.google.auto.value.AutoValue;
26 
27 /**
28  * Convenience data structure.
29  *
30  * <p>This differs from {@link ShortcutInfo} in that it doesn't hold an icon or intent, and provides
31  * convenience methods for doing things like constructing labels.
32  */
33 @TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
34 @AutoValue
35 abstract class DialerShortcut {
36 
37   /** Marker value indicates that shortcut has no setRank. Used by pinned shortcuts. */
38   static final int NO_RANK = -1;
39 
40   /**
41    * Contact ID from contacts provider. Note that this a numeric row ID from the
42    * ContactsContract.Contacts._ID column.
43    */
getContactId()44   abstract long getContactId();
45 
46   /**
47    * Lookup key from contacts provider. An example lookup key is: "0r8-47392D". This is the value
48    * from ContactsContract.Contacts.LOOKUP_KEY.
49    */
50   @NonNull
getLookupKey()51   abstract String getLookupKey();
52 
53   /** Display name from contacts provider. */
54   @NonNull
getDisplayName()55   abstract String getDisplayName();
56 
57   /**
58    * Rank for dynamic shortcuts. This value should be positive or {@link #NO_RANK}.
59    *
60    * <p>For floating shortcuts (pinned shortcuts with no corresponding dynamic shortcut), setRank
61    * has no meaning and the setRank may be set to {@link #NO_RANK}.
62    */
getRank()63   abstract int getRank();
64 
65   /** The short label for the shortcut. Used when pinning shortcuts, for example. */
66   @NonNull
getShortLabel()67   String getShortLabel() {
68     // Be sure to update getDisplayNameFromShortcutInfo when updating this.
69     return getDisplayName();
70   }
71 
72   /**
73    * The long label for the shortcut. Used for shortcuts displayed when pressing and holding the app
74    * launcher icon, for example.
75    */
76   @NonNull
getLongLabel()77   String getLongLabel() {
78     return getDisplayName();
79   }
80 
81   /** The display name for the provided shortcut. */
getDisplayNameFromShortcutInfo(ShortcutInfo shortcutInfo)82   static String getDisplayNameFromShortcutInfo(ShortcutInfo shortcutInfo) {
83     return shortcutInfo.getShortLabel().toString();
84   }
85 
86   /**
87    * The id used to identify launcher shortcuts. Used for updating/deleting shortcuts.
88    *
89    * <p>Lookup keys are used for shortcut IDs. See {@link #getLookupKey()}.
90    *
91    * <p>If you change this, you probably also need to change {@link #getLookupKeyFromShortcutInfo}.
92    */
93   @NonNull
getShortcutId()94   String getShortcutId() {
95     return getLookupKey();
96   }
97 
98   /**
99    * Returns the contact lookup key from the provided {@link ShortcutInfo}.
100    *
101    * <p>Lookup keys are used for shortcut IDs. See {@link #getLookupKey()}.
102    */
103   @NonNull
getLookupKeyFromShortcutInfo(@onNull ShortcutInfo shortcutInfo)104   static String getLookupKeyFromShortcutInfo(@NonNull ShortcutInfo shortcutInfo) {
105     return shortcutInfo.getId(); // Lookup keys are used for shortcut IDs.
106   }
107 
108   /**
109    * Returns the lookup URI from the provided {@link ShortcutInfo}.
110    *
111    * <p>Lookup URIs are constructed from lookup key and contact ID. Here is an example lookup URI
112    * where lookup key is "0r8-47392D" and contact ID is 8:
113    *
114    * <p>"content://com.android.contacts/contacts/lookup/0r8-47392D/8"
115    */
116   @NonNull
getLookupUriFromShortcutInfo(@onNull ShortcutInfo shortcutInfo)117   static Uri getLookupUriFromShortcutInfo(@NonNull ShortcutInfo shortcutInfo) {
118     long contactId =
119         shortcutInfo.getIntent().getLongExtra(ShortcutInfoFactory.EXTRA_CONTACT_ID, -1);
120     if (contactId == -1) {
121       throw new IllegalStateException("No contact ID found for shortcut: " + shortcutInfo.getId());
122     }
123     String lookupKey = getLookupKeyFromShortcutInfo(shortcutInfo);
124     return Contacts.getLookupUri(contactId, lookupKey);
125   }
126 
127   /**
128    * Contacts provider URI which uses the contact lookup key.
129    *
130    * <p>Lookup URIs are constructed from lookup key and contact ID. Here is an example lookup URI
131    * where lookup key is "0r8-47392D" and contact ID is 8:
132    *
133    * <p>"content://com.android.contacts/contacts/lookup/0r8-47392D/8"
134    */
135   @NonNull
getLookupUri()136   Uri getLookupUri() {
137     return Contacts.getLookupUri(getContactId(), getLookupKey());
138   }
139 
140   /**
141    * Given an existing shortcut with the same shortcut ID, returns true if the existing shortcut
142    * needs to be updated, e.g. if the contact's name or rank has changed.
143    *
144    * <p>Does not detect photo updates.
145    */
needsUpdate(@onNull ShortcutInfo oldInfo)146   boolean needsUpdate(@NonNull ShortcutInfo oldInfo) {
147     if (this.getRank() != NO_RANK && oldInfo.getRank() != this.getRank()) {
148       return true;
149     }
150     if (!oldInfo.getShortLabel().equals(this.getShortLabel())) {
151       return true;
152     }
153     if (!oldInfo.getLongLabel().equals(this.getLongLabel())) {
154       return true;
155     }
156     return false;
157   }
158 
builder()159   static Builder builder() {
160     return new AutoValue_DialerShortcut.Builder().setRank(NO_RANK);
161   }
162 
163   @AutoValue.Builder
164   abstract static class Builder {
165 
166     /**
167      * Sets the contact ID. This should be a value from the contact provider's Contact._ID column.
168      */
setContactId(long value)169     abstract Builder setContactId(long value);
170 
171     /**
172      * Sets the lookup key. This should be a contact lookup key as provided by the contact provider.
173      */
setLookupKey(@onNull String value)174     abstract Builder setLookupKey(@NonNull String value);
175 
176     /** Sets the display name. This should be a value provided by the contact provider. */
setDisplayName(@onNull String value)177     abstract Builder setDisplayName(@NonNull String value);
178 
179     /**
180      * Sets the rank for the shortcut, used for ordering dynamic shortcuts. This is required for
181      * dynamic shortcuts but unused for floating shortcuts because rank has no meaning for floating
182      * shortcuts. (Floating shortcuts are shortcuts which are pinned but have no corresponding
183      * dynamic shortcut.)
184      */
setRank(int value)185     abstract Builder setRank(int value);
186 
187     /** Builds the immutable {@link DialerShortcut} object from this builder. */
build()188     abstract DialerShortcut build();
189   }
190 }
191