Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for dynamic app shortcuts #4

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ public class SampleApplication extends Application {
public void onCreate() {
super.onCreate();

Shortbread.create(this);
// white listed
Shortbread.generate(this)
.disableAll()
.setEnabled(R.id.books, true)
.setEnabled(R.id.favorite_books, true)
.build();

// black listed
// Shortbread.generate(this)
// .setEnabled(R.id.books, false)
// .setEnabled(R.id.favorite_books, false)
// .build();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import shortbread.Shortcut;

@Shortcut(id = "books", icon = R.drawable.ic_shortcut_books, shortLabelRes = R.string.label_books, rank = 1)
@Shortcut(id = R.id.books, icon = R.drawable.ic_shortcut_books, shortLabelRes = R.string.label_books, rank = 1)
public class BooksActivity extends Activity {

@Override
Expand All @@ -17,7 +17,7 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_books);
}

@Shortcut(id = "favorite_books", icon = R.drawable.ic_shortcut_favorite, shortLabel = "Favorite books", rank = 2,
@Shortcut(id = R.id.favorite_books, icon = R.drawable.ic_shortcut_favorite, shortLabel = "Favorite books", rank = 2,
disabledMessage = "You have no favorite books")
public void showFavoriteBooks() {
Toast.makeText(this, "Favorite books", Toast.LENGTH_SHORT).show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import shortbread.Shortcut;

@Shortcut(id = "movies", action = "movie_shortcut", icon = R.drawable.ic_shortcut_movies, rank = 3,
@Shortcut(id = R.id.movies, action = "movie_shortcut", icon = R.drawable.ic_shortcut_movies, rank = 3,
backStack = {MainActivity.class, BooksActivity.class})
public class MoviesActivity extends Activity {

Expand All @@ -20,7 +20,7 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_movies);
}

@Shortcut(id = "add_movie", icon = R.drawable.ic_shortcut_add, shortLabel = "Add movie", rank = 4, disabledMessageRes = R.string.label_books)
@Shortcut(id = R.id.add_movies, icon = R.drawable.ic_shortcut_add, shortLabel = "Add movie", rank = 4, disabledMessageRes = R.string.label_books)
public void addMovie() {
Toast.makeText(this, "Add movie", Toast.LENGTH_SHORT).show();
}
Expand Down
7 changes: 7 additions & 0 deletions sample/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="movies" type="id" />
<item name="add_movies" type="id" />
<item name="books" type="id" />
<item name="favorite_books" type="id" />
</resources>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package shortbread;

import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.support.annotation.IntRange;
import android.support.annotation.StringRes;

Expand All @@ -10,7 +11,8 @@
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Shortcut {

String id();
@IdRes
int id();

String shortLabel() default "";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -38,7 +37,6 @@ class CodeGenerator {
private final ClassName componentName = ClassName.get("android.content", "ComponentName");
private final ClassName list = ClassName.get("java.util", "List");
private final TypeName listOfShortcutInfo = ParameterizedTypeName.get(list, shortcutInfo);
private final TypeName listOfListOfShortcutInfo = ParameterizedTypeName.get(list, listOfShortcutInfo);
private final ClassName taskStackBuilder = ClassName.get("android.app", "TaskStackBuilder");
private final ClassName shortcutUtils = ClassName.get("shortbread", "ShortcutUtils");

Expand Down Expand Up @@ -75,10 +73,9 @@ void generate() {
private MethodSpec createShortcuts() {
final MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("createShortcuts")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(listOfListOfShortcutInfo)
.returns(listOfShortcutInfo)
.addParameter(context, "context")
.addStatement("$T<$T> enabledShortcuts = new $T<>()", List.class, shortcutInfo, ArrayList.class)
.addStatement("$T<$T> disabledShortcuts = new $T<>()", List.class, shortcutInfo, ArrayList.class);
.addStatement("$T<$T> enabledShortcuts = new $T<>()", List.class, shortcutInfo, ArrayList.class);

for (final ShortcutAnnotatedElement annotatedElement : annotatedElements) {
Shortcut shortcut = annotatedElement.getShortcut();
Expand All @@ -88,7 +85,7 @@ private MethodSpec createShortcuts() {
}

return methodBuilder
.addStatement("return $T.asList(enabledShortcuts, disabledShortcuts)", Arrays.class)
.addStatement("return enabledShortcuts")
.build();
}

Expand Down
89 changes: 89 additions & 0 deletions shortbread/src/main/java/shortbread/Creator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package shortbread;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

class Creator {

private static boolean activityLifecycleCallbacksSet;
private static boolean shortcutsSet;


@TargetApi(25)
static void create(Context context, List<ShortcutInfo> allShortcuts, boolean[] shortCutStatus) {
if (Build.VERSION.SDK_INT < 25) {
return;
}

Context applicationContext = context.getApplicationContext();

if (!shortcutsSet) {
setShortcuts(applicationContext, allShortcuts, shortCutStatus);
}

if (!activityLifecycleCallbacksSet) {
setActivityLifecycleCallbacks(applicationContext);
}

if (context instanceof Activity) {
callMethodShortcut((Activity) context);
}
}

@RequiresApi(14)
private static void setActivityLifecycleCallbacks(Context applicationContext) {
((Application) applicationContext).registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {

@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) {
callMethodShortcut(activity);
}
});

activityLifecycleCallbacksSet = true;
}

@RequiresApi(25)
private static void setShortcuts(Context context, List<ShortcutInfo> allShortcuts, boolean[] shortcutStates) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);

//noinspection TryWithIdenticalCatches
shortcutManager.removeAllDynamicShortcuts();
shortcutManager.setDynamicShortcuts(allShortcuts);
List<String> disabledShortcuts = new ArrayList<>();
for (int i = 0; i < shortcutStates.length; i++) {
if (!shortcutStates[i]) {
disabledShortcuts.add(allShortcuts.get(i).getId());
}
}
shortcutManager.disableShortcuts(disabledShortcuts);
shortcutsSet = true;
}

private static void callMethodShortcut(Activity activity) {
//noinspection TryWithIdenticalCatches
try {
Method callMethodShortcut = Shortbread.generated.getMethod("callMethodShortcut", Activity.class);

callMethodShortcut.invoke(Shortbread.generated, activity);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
102 changes: 15 additions & 87 deletions shortbread/src/main/java/shortbread/Shortbread.java
Original file line number Diff line number Diff line change
@@ -1,110 +1,38 @@
package shortbread;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.annotation.NonNull;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public final class Shortbread {

private static Class<?> generated;
private static Method createShortcuts;
private static Method callMethodShortcut;
private static boolean shortcutsSet;
private static boolean activityLifecycleCallbacksSet;
static Class<?> generated;

@TargetApi(25)
public static void create(Context context) {
if (Build.VERSION.SDK_INT < 25) {
return;
}

Context applicationContext = context.getApplicationContext();

if (generated == null) {
//noinspection TryWithIdenticalCatches
try {
generated = Class.forName("shortbread.ShortbreadGenerated");
createShortcuts = generated.getMethod("createShortcuts", Context.class);
callMethodShortcut = generated.getMethod("callMethodShortcut", Activity.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}

if (!shortcutsSet) {
setShortcuts(applicationContext);
}

if (!activityLifecycleCallbacksSet) {
setActivityLifecycleCallbacks(applicationContext);
}

if (context instanceof Activity) {
callMethodShortcut((Activity) context);
}
}

@RequiresApi(25)
private static void setShortcuts(Context context) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
@NonNull
public static ShortbreadInitialBuilder generate(Context context) {
final Object returnValue;

//noinspection TryWithIdenticalCatches
try {
final Object returnValue = createShortcuts.invoke(generated, context);
@SuppressWarnings("unchecked")
List<List<ShortcutInfo>> shortcuts = (List<List<ShortcutInfo>>) returnValue;
List<ShortcutInfo> enabledShortcuts = shortcuts.get(0);
List<String> disabledShortcutsIds = new ArrayList<>();
for (final ShortcutInfo shortcutInfo : shortcuts.get(1)) {
disabledShortcutsIds.add(shortcutInfo.getId());
generated = Class.forName("shortbread.ShortbreadGenerated");
Method createShortcuts = generated.getMethod("createShortcuts", Context.class);
returnValue = createShortcuts.invoke(generated, context);
boolean[] shortcutStatus = new boolean[((List<ShortcutInfo>) returnValue).size()];
// enable all shortcuts by default
for (int i = 0; i < shortcutStatus.length; i++) {
shortcutStatus[i] = true;
}
shortcutManager.disableShortcuts(disabledShortcutsIds);
shortcutManager.setDynamicShortcuts(enabledShortcuts);
shortcutsSet = true;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

@RequiresApi(14)
private static void setActivityLifecycleCallbacks(Context applicationContext) {
((Application) applicationContext).registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {

@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) {
callMethodShortcut(activity);
}
});

activityLifecycleCallbacksSet = true;
}
return new ShortbreadInitialBuilder(context, (List<ShortcutInfo>) returnValue, shortcutStatus);

private static void callMethodShortcut(Activity activity) {
//noinspection TryWithIdenticalCatches
try {
callMethodShortcut.invoke(generated, activity);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
throw new IllegalStateException();
}

private Shortbread() {
}
}
49 changes: 49 additions & 0 deletions shortbread/src/main/java/shortbread/ShortbreadBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package shortbread;

import android.content.Context;
import android.content.pm.ShortcutInfo;

import java.util.ArrayList;
import java.util.List;

public class ShortbreadBuilder {

Context context;
List<ShortcutInfo> allShortcuts = new ArrayList<>();
boolean[] shortcutStatus;

ShortbreadBuilder(Context context, List<ShortcutInfo> allShortcuts, boolean[] shortcutStatus) {

this.allShortcuts = allShortcuts;
this.shortcutStatus = shortcutStatus;
this.context = context;
}

/**
* Change the status for the app shortcut with the given {@code id}.
*
* @param id the app shortcut id
* @param enabled true, if the shortcut should be enabled, otherwise false.
* @return shortbread builder
*/
public ShortbreadBuilder setEnabled(int id, boolean enabled) {
shortcutStatus[getIndex(id)] = enabled;
return this;
}

/**
* Build the shortcuts
*/
public void build() {
Creator.create(context, allShortcuts, shortcutStatus);
}

private int getIndex(int id) {
for (int i = 0; i < allShortcuts.size(); i++) {
if (Integer.valueOf(allShortcuts.get(i).getId()) == id) {
return i;
}
}
return -1;
}
}
Loading