diff --git a/app/build.gradle b/app/build.gradle index 0bcaebec4..98cf5bc1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { defaultConfig { applicationId "protect.card_locker" minSdkVersion 17 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 14 versionName "0.13" } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 3bd10c4ed..89d004e44 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -354,6 +354,9 @@ public class LoyaltyCardViewActivity extends AppCompatActivity DBHelper db = new DBHelper(LoyaltyCardViewActivity.this); db.deleteLoyaltyCard(loyaltyCardId); + + ShortcutHelper.removeShortcut(LoyaltyCardViewActivity.this, loyaltyCardId); + finish(); dialog.dismiss(); } diff --git a/app/src/main/java/protect/card_locker/MainActivity.java b/app/src/main/java/protect/card_locker/MainActivity.java index 7eb745794..2bf8dafaa 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.java +++ b/app/src/main/java/protect/card_locker/MainActivity.java @@ -7,7 +7,10 @@ import android.content.ClipboardManager; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.database.Cursor; +import android.os.Build; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; @@ -27,6 +30,7 @@ import android.widget.Toast; import com.google.common.collect.ImmutableMap; import java.util.Calendar; +import java.util.List; import java.util.Map; import protect.card_locker.intro.IntroActivity; @@ -93,10 +97,14 @@ public class MainActivity extends AppCompatActivity LoyaltyCard loyaltyCard = LoyaltyCard.toLoyaltyCard(selected); Intent i = new Intent(view.getContext(), LoyaltyCardViewActivity.class); + i.setAction(""); final Bundle b = new Bundle(); b.putInt("id", loyaltyCard.id); b.putBoolean("view", true); i.putExtras(b); + + ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard, i); + startActivity(i); } }); diff --git a/app/src/main/java/protect/card_locker/ShortcutHelper.java b/app/src/main/java/protect/card_locker/ShortcutHelper.java new file mode 100644 index 000000000..0b9dbc137 --- /dev/null +++ b/app/src/main/java/protect/card_locker/ShortcutHelper.java @@ -0,0 +1,147 @@ +package protect.card_locker; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.drawable.Icon; +import android.os.Build; + +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +class ShortcutHelper +{ + // Android documentation says that no more than 5 shortcuts + // are supported. However, that may be too many, as not all + // launcher will show all 5. Instead, the number is limited + // to 3 here, so that the most recent shortcut has a good + // chance of being shown. + private static final int MAX_SHORTCUTS = 3; + + /** + * Add a card to the app shortcuts, and maintain a list of the most + * recently used cards. If there is already a shortcut for the card, + * the card is marked as the most recently used card. If adding this + * card exceeds the max number of shortcuts, then the least recently + * used card shortcut is discarded. + */ + @TargetApi(25) + static void updateShortcuts(Context context, LoyaltyCard card, Intent intent) + { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) + { + ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); + LinkedList list = new LinkedList<>(shortcutManager.getDynamicShortcuts()); + + String shortcutId = Integer.toString(card.id); + + // Sort the shortcuts by rank, so working with the relative order will be easier. + // This sorts so that the lowest rank is first. + Collections.sort(list, new Comparator() + { + @Override + public int compare(ShortcutInfo o1, ShortcutInfo o2) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) + { + return o1.getRank() - o2.getRank(); + } + else + { + return 0; + } + } + }); + + Integer foundIndex = null; + + for(int index = 0; index < list.size(); index++) + { + if(list.get(index).getId().equals(shortcutId)) + { + // Found the item already + foundIndex = index; + break; + } + } + + if(foundIndex != null) + { + // If the item is already found, then the list needs to be + // reordered, so that the selected item now has the lowest + // rank, thus letting it survive longer. + ShortcutInfo found = list.remove(foundIndex.intValue()); + list.addFirst(found); + } + else + { + // The item is new to the list. First, we need to trim the list + // until it is able to accept a new item, then the item is + // inserted. + while(list.size() >= MAX_SHORTCUTS) + { + list.pollLast(); + } + + ShortcutInfo shortcut = new ShortcutInfo.Builder(context, Integer.toString(card.id)) + .setShortLabel(card.store) + .setLongLabel(card.store) + .setIntent(intent) + .build(); + + list.addFirst(shortcut); + } + + LinkedList finalList = new LinkedList<>(); + + // The ranks are now updated; the order in the list is the rank. + for(int index = 0; index < list.size(); index++) + { + ShortcutInfo prevShortcut = list.get(index); + + ShortcutInfo updatedShortcut = new ShortcutInfo.Builder(context, prevShortcut.getId()) + .setShortLabel(prevShortcut.getShortLabel()) + .setLongLabel(prevShortcut.getLongLabel()) + .setIntent(prevShortcut.getIntent()) + .setIcon(Icon.createWithResource(context, R.drawable.circle)) + .setRank(index) + .build(); + + finalList.addLast(updatedShortcut); + } + + shortcutManager.setDynamicShortcuts(finalList); + } + } + + /** + * Remove the given card id from the app shortcuts, if such a + * shortcut exists. + */ + @TargetApi(25) + static void removeShortcut(Context context, int cardId) + { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) + { + ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); + List list = shortcutManager.getDynamicShortcuts(); + + String shortcutId = Integer.toString(cardId); + + for(int index = 0; index < list.size(); index++) + { + if(list.get(index).getId().equals(shortcutId)) + { + list.remove(index); + break; + } + } + + shortcutManager.setDynamicShortcuts(list); + } + } +} diff --git a/app/src/main/res/drawable-nodpi/circle.png b/app/src/main/res/drawable-nodpi/circle.png new file mode 100644 index 000000000..4cd4dd58b Binary files /dev/null and b/app/src/main/res/drawable-nodpi/circle.png differ