use of com.google.android.apps.muzei.api.UserCommand in project muzei by romannurik.
the class NewWallpaperNotificationReceiver method triggerUserCommandFromRemoteInput.
private void triggerUserCommandFromRemoteInput(Context context, Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput == null) {
return;
}
String selectedCommand = remoteInput.getCharSequence(EXTRA_USER_COMMAND).toString();
Cursor selectedSource = context.getContentResolver().query(MuzeiContract.Sources.CONTENT_URI, new String[] { MuzeiContract.Sources.COLUMN_NAME_COMMANDS }, MuzeiContract.Sources.COLUMN_NAME_IS_SELECTED + "=1", null, null, null);
if (selectedSource != null && selectedSource.moveToFirst()) {
List<UserCommand> commands = MuzeiContract.Sources.parseCommands(selectedSource.getString(0));
for (UserCommand action : commands) {
if (TextUtils.equals(selectedCommand, action.getTitle())) {
SourceManager.sendAction(context, action.getId());
break;
}
}
}
if (selectedSource != null) {
selectedSource.close();
}
}
use of com.google.android.apps.muzei.api.UserCommand in project muzei by romannurik.
the class SourceManager method migrateDataToContentProvider.
/**
* One time migration of source data from SharedPreferences to the ContentProvider
*/
private static void migrateDataToContentProvider(Context context) {
SharedPreferences sharedPrefs = context.getSharedPreferences("muzei_art_sources", 0);
String selectedSourceString = sharedPrefs.getString(PREF_SELECTED_SOURCE, null);
Set<String> sourceStates = sharedPrefs.getStringSet(PREF_SOURCE_STATES, null);
if (selectedSourceString == null || sourceStates == null) {
return;
}
ComponentName selectedSource = ComponentName.unflattenFromString(selectedSourceString);
final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
for (String sourceStatesPair : sourceStates) {
String[] pair = sourceStatesPair.split("\\|", 2);
ComponentName source = ComponentName.unflattenFromString(pair[0]);
try {
ContentValues values = new ContentValues();
try {
// Ensure the source is a valid Service
context.getPackageManager().getServiceInfo(source, 0);
} catch (PackageManager.NameNotFoundException e) {
// No need to keep no longer valid sources
continue;
}
values.put(MuzeiContract.Sources.COLUMN_NAME_COMPONENT_NAME, source.flattenToShortString());
values.put(MuzeiContract.Sources.COLUMN_NAME_IS_SELECTED, source.equals(selectedSource));
JSONObject jsonObject = (JSONObject) new JSONTokener(pair[1]).nextValue();
values.put(MuzeiContract.Sources.COLUMN_NAME_DESCRIPTION, jsonObject.optString("description"));
values.put(MuzeiContract.Sources.COLUMN_NAME_WANTS_NETWORK_AVAILABLE, jsonObject.optBoolean("wantsNetworkAvailable"));
// Parse out the UserCommands. This ensures it is properly formatted and extracts the
// Next Artwork built in command from the list
List<UserCommand> commands = MuzeiContract.Sources.parseCommands(jsonObject.optJSONArray("userCommands").toString());
JSONArray commandsSerialized = new JSONArray();
boolean supportsNextArtwork = false;
for (UserCommand command : commands) {
if (command.getId() == MuzeiArtSource.BUILTIN_COMMAND_ID_NEXT_ARTWORK) {
supportsNextArtwork = true;
} else {
commandsSerialized.put(command.serialize());
}
}
values.put(MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND, supportsNextArtwork);
values.put(MuzeiContract.Sources.COLUMN_NAME_COMMANDS, commandsSerialized.toString());
operations.add(ContentProviderOperation.newInsert(MuzeiContract.Sources.CONTENT_URI).withValues(values).build());
} catch (JSONException e) {
Log.e(TAG, "Error loading source state for " + source, e);
}
}
try {
context.getContentResolver().applyBatch(MuzeiContract.AUTHORITY, operations);
sharedPrefs.edit().remove(PREF_SELECTED_SOURCE).remove(PREF_SOURCE_STATES).apply();
sendSelectedSourceAnalytics(context, selectedSource);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Error writing sources to ContentProvider", e);
}
}
use of com.google.android.apps.muzei.api.UserCommand in project muzei by romannurik.
the class SourceSubscriberService method onHandleIntent.
@Override
protected void onHandleIntent(Intent intent) {
if (intent == null || intent.getAction() == null) {
return;
}
String action = intent.getAction();
if (!ACTION_PUBLISH_STATE.equals(action)) {
return;
}
// Handle API call from source
String token = intent.getStringExtra(EXTRA_TOKEN);
ComponentName selectedSource = SourceManager.getSelectedSource(this);
if (selectedSource == null || !TextUtils.equals(token, selectedSource.flattenToShortString())) {
Log.w(TAG, "Dropping update from non-selected source, token=" + token + " does not match token for " + selectedSource);
return;
}
SourceState state = null;
if (intent.hasExtra(EXTRA_STATE)) {
Bundle bundle = intent.getBundleExtra(EXTRA_STATE);
if (bundle != null) {
state = SourceState.fromBundle(bundle);
}
}
if (state == null) {
// If there is no state, there is nothing to change
return;
}
ContentValues values = new ContentValues();
values.put(MuzeiContract.Sources.COLUMN_NAME_COMPONENT_NAME, selectedSource.flattenToShortString());
values.put(MuzeiContract.Sources.COLUMN_NAME_IS_SELECTED, true);
values.put(MuzeiContract.Sources.COLUMN_NAME_DESCRIPTION, state.getDescription());
values.put(MuzeiContract.Sources.COLUMN_NAME_WANTS_NETWORK_AVAILABLE, state.getWantsNetworkAvailable());
JSONArray commandsSerialized = new JSONArray();
int numSourceActions = state.getNumUserCommands();
boolean supportsNextArtwork = false;
for (int i = 0; i < numSourceActions; i++) {
UserCommand command = state.getUserCommandAt(i);
if (command.getId() == MuzeiArtSource.BUILTIN_COMMAND_ID_NEXT_ARTWORK) {
supportsNextArtwork = true;
} else {
commandsSerialized.put(command.serialize());
}
}
values.put(MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND, supportsNextArtwork);
values.put(MuzeiContract.Sources.COLUMN_NAME_COMMANDS, commandsSerialized.toString());
ContentResolver contentResolver = getContentResolver();
Cursor existingSource = contentResolver.query(MuzeiContract.Sources.CONTENT_URI, new String[] { BaseColumns._ID }, MuzeiContract.Sources.COLUMN_NAME_COMPONENT_NAME + "=?", new String[] { selectedSource.flattenToShortString() }, null, null);
if (existingSource != null && existingSource.moveToFirst()) {
Uri sourceUri = ContentUris.withAppendedId(MuzeiContract.Sources.CONTENT_URI, existingSource.getLong(0));
contentResolver.update(sourceUri, values, null, null);
} else {
contentResolver.insert(MuzeiContract.Sources.CONTENT_URI, values);
}
if (existingSource != null) {
existingSource.close();
}
Artwork artwork = state.getCurrentArtwork();
if (artwork != null) {
artwork.setComponentName(selectedSource);
contentResolver.insert(MuzeiContract.Artwork.CONTENT_URI, artwork.toContentValues());
// Download the artwork contained from the newly published SourceState
startService(TaskQueueService.getDownloadCurrentArtworkIntent(this));
}
}
use of com.google.android.apps.muzei.api.UserCommand in project muzei by romannurik.
the class NewWallpaperNotificationReceiver method maybeShowNewArtworkNotification.
public static void maybeShowNewArtworkNotification(Context context) {
ArtDetailOpenedClosedEvent adoce = EventBus.getDefault().getStickyEvent(ArtDetailOpenedClosedEvent.class);
if (adoce != null && adoce.isArtDetailOpened()) {
return;
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
if (!sp.getBoolean(PREF_ENABLED, true)) {
return;
}
ContentResolver contentResolver = context.getContentResolver();
Cursor artwork = contentResolver.query(MuzeiContract.Artwork.CONTENT_URI, new String[] { BaseColumns._ID, MuzeiContract.Artwork.COLUMN_NAME_IMAGE_URI, MuzeiContract.Artwork.COLUMN_NAME_TOKEN, MuzeiContract.Artwork.COLUMN_NAME_TITLE, MuzeiContract.Artwork.COLUMN_NAME_BYLINE, MuzeiContract.Artwork.COLUMN_NAME_VIEW_INTENT, MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND, MuzeiContract.Sources.COLUMN_NAME_COMMANDS }, null, null, null);
if (artwork == null || !artwork.moveToFirst()) {
if (artwork != null) {
artwork.close();
}
return;
}
long currentArtworkId = artwork.getLong(artwork.getColumnIndex(BaseColumns._ID));
long lastReadArtworkId = sp.getLong(PREF_LAST_READ_NOTIFICATION_ARTWORK_ID, -1);
String currentImageUri = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_IMAGE_URI));
String lastReadImageUri = sp.getString(PREF_LAST_READ_NOTIFICATION_ARTWORK_IMAGE_URI, null);
String currentToken = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_TOKEN));
String lastReadToken = sp.getString(PREF_LAST_READ_NOTIFICATION_ARTWORK_TOKEN, null);
// We've already dismissed the notification if the IDs match
boolean previouslyDismissedNotification = lastReadArtworkId == currentArtworkId;
// We've already dismissed the notification if the image URIs match and both are not empty
previouslyDismissedNotification = previouslyDismissedNotification || (!TextUtils.isEmpty(lastReadImageUri) && !TextUtils.isEmpty(currentImageUri) && TextUtils.equals(lastReadImageUri, currentImageUri));
// We've already dismissed the notification if the tokens match and both are not empty
previouslyDismissedNotification = previouslyDismissedNotification || (!TextUtils.isEmpty(lastReadToken) && !TextUtils.isEmpty(currentToken) && TextUtils.equals(lastReadToken, currentToken));
if (previouslyDismissedNotification) {
artwork.close();
return;
}
Bitmap largeIcon;
Bitmap background;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(contentResolver.openInputStream(MuzeiContract.Artwork.CONTENT_URI), null, options);
int width = options.outWidth;
int height = options.outHeight;
int shortestLength = Math.min(width, height);
options.inJustDecodeBounds = false;
int largeIconHeight = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
options.inSampleSize = ImageUtil.calculateSampleSize(shortestLength, largeIconHeight);
largeIcon = BitmapFactory.decodeStream(contentResolver.openInputStream(MuzeiContract.Artwork.CONTENT_URI), null, options);
// Use the suggested 400x400 for Android Wear background images per
// http://developer.android.com/training/wearables/notifications/creating.html#AddWearableFeatures
options.inSampleSize = ImageUtil.calculateSampleSize(height, 400);
background = BitmapFactory.decodeStream(contentResolver.openInputStream(MuzeiContract.Artwork.CONTENT_URI), null, options);
} catch (FileNotFoundException e) {
Log.e(TAG, "Unable to read artwork to show notification", e);
return;
}
String artworkTitle = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_TITLE));
String title = TextUtils.isEmpty(artworkTitle) ? context.getString(R.string.app_name) : artworkTitle;
NotificationCompat.Builder nb = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_stat_muzei).setColor(ContextCompat.getColor(context, R.color.notification)).setPriority(Notification.PRIORITY_MIN).setAutoCancel(true).setContentTitle(title).setContentText(context.getString(R.string.notification_new_wallpaper)).setLargeIcon(largeIcon).setContentIntent(PendingIntent.getActivity(context, 0, Intent.makeMainActivity(new ComponentName(context, MuzeiActivity.class)), PendingIntent.FLAG_UPDATE_CURRENT)).setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(context, NewWallpaperNotificationReceiver.class).setAction(ACTION_MARK_NOTIFICATION_READ), PendingIntent.FLAG_UPDATE_CURRENT));
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle().bigLargeIcon(null).setBigContentTitle(title).setSummaryText(artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_BYLINE))).bigPicture(background);
nb.setStyle(style);
NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender();
// Support Next Artwork
if (artwork.getInt(artwork.getColumnIndex(MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND)) != 0) {
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, NewWallpaperNotificationReceiver.class).setAction(ACTION_NEXT_ARTWORK), PendingIntent.FLAG_UPDATE_CURRENT);
nb.addAction(R.drawable.ic_notif_next_artwork, context.getString(R.string.action_next_artwork_condensed), nextPendingIntent);
// Android Wear uses larger action icons so we build a
// separate action
extender.addAction(new NotificationCompat.Action.Builder(R.drawable.ic_notif_full_next_artwork, context.getString(R.string.action_next_artwork_condensed), nextPendingIntent).extend(new NotificationCompat.Action.WearableExtender().setAvailableOffline(false)).build());
}
List<UserCommand> commands = MuzeiContract.Sources.parseCommands(artwork.getString(artwork.getColumnIndex(MuzeiContract.Sources.COLUMN_NAME_COMMANDS)));
// Show custom actions as a selectable list on Android Wear devices
if (!commands.isEmpty()) {
String[] actions = new String[commands.size()];
for (int h = 0; h < commands.size(); h++) {
actions[h] = commands.get(h).getTitle();
}
PendingIntent userCommandPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, NewWallpaperNotificationReceiver.class).setAction(ACTION_USER_COMMAND), PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_USER_COMMAND).setAllowFreeFormInput(false).setLabel(context.getString(R.string.action_user_command_prompt)).setChoices(actions).build();
extender.addAction(new NotificationCompat.Action.Builder(R.drawable.ic_notif_full_user_command, context.getString(R.string.action_user_command), userCommandPendingIntent).addRemoteInput(remoteInput).extend(new NotificationCompat.Action.WearableExtender().setAvailableOffline(false)).build());
}
Intent viewIntent = null;
try {
String viewIntentString = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_VIEW_INTENT));
if (!TextUtils.isEmpty(viewIntentString)) {
viewIntent = Intent.parseUri(viewIntentString, Intent.URI_INTENT_SCHEME);
}
} catch (URISyntaxException ignored) {
}
if (viewIntent != null) {
viewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
PendingIntent nextPendingIntent = PendingIntent.getActivity(context, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT);
nb.addAction(R.drawable.ic_notif_info, context.getString(R.string.action_artwork_info), nextPendingIntent);
// Android Wear uses larger action icons so we build a
// separate action
extender.addAction(new NotificationCompat.Action.Builder(R.drawable.ic_notif_full_info, context.getString(R.string.action_artwork_info), nextPendingIntent).extend(new NotificationCompat.Action.WearableExtender().setAvailableOffline(false)).build());
} catch (RuntimeException ignored) {
// This is actually meant to catch a FileUriExposedException, but you can't
// have catch statements for exceptions that don't exist at your minSdkVersion
}
}
nb.extend(extender);
// Hide the image and artwork title for the public version
NotificationCompat.Builder publicBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_stat_muzei).setColor(ContextCompat.getColor(context, R.color.notification)).setPriority(Notification.PRIORITY_MIN).setAutoCancel(true).setContentTitle(context.getString(R.string.app_name)).setContentText(context.getString(R.string.notification_new_wallpaper)).setContentIntent(PendingIntent.getActivity(context, 0, Intent.makeMainActivity(new ComponentName(context, MuzeiActivity.class)), PendingIntent.FLAG_UPDATE_CURRENT)).setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(context, NewWallpaperNotificationReceiver.class).setAction(ACTION_MARK_NOTIFICATION_READ), PendingIntent.FLAG_UPDATE_CURRENT));
nb.setPublicVersion(publicBuilder.build());
NotificationManagerCompat nm = NotificationManagerCompat.from(context);
nm.notify(NOTIFICATION_ID, nb.build());
artwork.close();
}
use of com.google.android.apps.muzei.api.UserCommand in project muzei by romannurik.
the class FeaturedArtSource method onUpdate.
@Override
protected void onUpdate(@UpdateReason int reason) {
List<UserCommand> commands = new ArrayList<>();
if (reason == UPDATE_REASON_INITIAL) {
// Show initial photo (starry night)
publishArtwork(new Artwork.Builder().imageUri(Uri.parse("file:///android_asset/starrynight.jpg")).title("The Starry Night").token("initial").byline("Vincent van Gogh, 1889.\nMuzei shows a new painting every day.").attribution("wikiart.org").viewIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.wikiart.org/en/vincent-van-gogh/the-starry-night-1889"))).metaFont(MuzeiContract.Artwork.META_FONT_TYPE_ELEGANT).build());
commands.add(new UserCommand(BUILTIN_COMMAND_ID_NEXT_ARTWORK));
// show the latest photo in 15 minutes
scheduleUpdate(System.currentTimeMillis() + 15 * 60 * 1000);
} else {
// For everything but the initial update, defer to RemoteMuzeiArtSource
super.onUpdate(reason);
}
commands.add(new UserCommand(COMMAND_ID_SHARE, getString(R.string.featuredart_action_share_artwork)));
commands.add(new UserCommand(COMMAND_ID_VIEW_ARCHIVE, getString(R.string.featuredart_source_action_view_archive)));
if (BuildConfig.DEBUG) {
commands.add(new UserCommand(COMMAND_ID_DEBUG_INFO, "Debug info"));
}
setUserCommands(commands);
}
Aggregations