Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an app widget with next artwork action
Allow quick access to a next artwork action through an expandable app widget that shows a non-blurred version of your background.
- Loading branch information
1 parent
b251ec6
commit 057062f
Showing
10 changed files
with
437 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
main/src/main/java/com/google/android/apps/muzei/widget/AppWidgetUpdateReceiver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright 2014 Google Inc. | ||
* | ||
* 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.google.android.apps.muzei.widget; | ||
|
||
import android.content.BroadcastReceiver; | ||
import android.content.Context; | ||
import android.content.Intent; | ||
import android.text.TextUtils; | ||
|
||
import com.google.android.apps.muzei.api.MuzeiContract; | ||
|
||
/** | ||
* Receive update broadcasts from Muzei and refreshes the widget. | ||
*/ | ||
public class AppWidgetUpdateReceiver extends BroadcastReceiver { | ||
@Override | ||
public void onReceive(Context context, Intent intent) { | ||
if (intent != null) { | ||
String action = intent.getAction(); | ||
if (TextUtils.equals(action, MuzeiContract.Artwork.ACTION_ARTWORK_CHANGED) || | ||
TextUtils.equals(action, MuzeiContract.Sources.ACTION_SOURCE_CHANGED)) { | ||
final PendingResult result = goAsync(); | ||
new AppWidgetUpdateTask(context) { | ||
@Override | ||
protected void onPostExecute(Boolean success) { | ||
result.finish(); | ||
} | ||
}.execute(); | ||
} | ||
} | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
main/src/main/java/com/google/android/apps/muzei/widget/AppWidgetUpdateTask.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* Copyright 2014 Google Inc. | ||
* | ||
* 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.google.android.apps.muzei.widget; | ||
|
||
import android.app.PendingIntent; | ||
import android.appwidget.AppWidgetManager; | ||
import android.content.ComponentName; | ||
import android.content.ContentResolver; | ||
import android.content.ContentUris; | ||
import android.content.Context; | ||
import android.content.Intent; | ||
import android.content.pm.PackageManager; | ||
import android.database.Cursor; | ||
import android.graphics.Bitmap; | ||
import android.graphics.BitmapFactory; | ||
import android.net.Uri; | ||
import android.os.AsyncTask; | ||
import android.os.Bundle; | ||
import android.provider.BaseColumns; | ||
import android.support.annotation.LayoutRes; | ||
import android.text.TextUtils; | ||
import android.util.DisplayMetrics; | ||
import android.util.Log; | ||
import android.util.TypedValue; | ||
import android.view.View; | ||
import android.widget.RemoteViews; | ||
|
||
import com.google.android.apps.muzei.api.MuzeiContract; | ||
import com.google.android.apps.muzei.render.ImageUtil; | ||
|
||
import net.nurik.roman.muzei.R; | ||
|
||
import java.io.FileNotFoundException; | ||
|
||
/** | ||
* Async operation used to update the AppWidget. | ||
*/ | ||
class AppWidgetUpdateTask extends AsyncTask<Void,Void,Boolean> { | ||
private static final String TAG = "AppWidgetUpdateTask"; | ||
|
||
private final Context mContext; | ||
|
||
AppWidgetUpdateTask(Context context) { | ||
mContext = context; | ||
} | ||
|
||
@Override | ||
protected Boolean doInBackground(Void... params) { | ||
ComponentName widget = new ComponentName(mContext, MuzeiAppWidgetProvider.class); | ||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); | ||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(widget); | ||
if (appWidgetIds.length == 0) { | ||
// No app widgets, nothing to do | ||
Log.i(TAG, "No AppWidgets found"); | ||
return true; | ||
} | ||
String[] projection = new String[] {BaseColumns._ID, | ||
MuzeiContract.Artwork.COLUMN_NAME_TITLE, | ||
MuzeiContract.Artwork.COLUMN_NAME_BYLINE, | ||
MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND}; | ||
ContentResolver contentResolver = mContext.getContentResolver(); | ||
Cursor artwork = contentResolver.query(MuzeiContract.Artwork.CONTENT_URI, projection, null, null, null); | ||
if (artwork == null || !artwork.moveToFirst()) { | ||
Log.w(TAG, "No current artwork found"); | ||
if (artwork != null) { | ||
artwork.close(); | ||
} | ||
return false; | ||
} | ||
String title = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_TITLE)); | ||
String byline = artwork.getString(artwork.getColumnIndex(MuzeiContract.Artwork.COLUMN_NAME_BYLINE)); | ||
String contentDescription = !TextUtils.isEmpty(title) | ||
? title | ||
: byline; | ||
Uri imageUri = ContentUris.withAppendedId(MuzeiContract.Artwork.CONTENT_URI, | ||
artwork.getLong(artwork.getColumnIndex(BaseColumns._ID))); | ||
boolean supportsNextArtwork = artwork.getInt( | ||
artwork.getColumnIndex(MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND)) != 0; | ||
artwork.close(); | ||
|
||
// Update the widget(s) with the new artwork information | ||
PackageManager packageManager = mContext.getPackageManager(); | ||
Intent launchIntent = packageManager.getLaunchIntentForPackage(mContext.getPackageName()); | ||
PendingIntent launchPendingIntent = PendingIntent.getActivity(mContext, | ||
0, launchIntent, PendingIntent.FLAG_UPDATE_CURRENT); | ||
Intent nextArtworkIntent = new Intent(mContext, MuzeiAppWidgetProvider.class); | ||
nextArtworkIntent.setAction(MuzeiAppWidgetProvider.ACTION_NEXT_ARTWORK); | ||
PendingIntent nextArtworkPendingIntent = PendingIntent.getBroadcast(mContext, | ||
0, nextArtworkIntent, PendingIntent.FLAG_UPDATE_CURRENT); | ||
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); | ||
int smallWidgetHeight = mContext.getResources().getDimensionPixelSize( | ||
R.dimen.widget_small_height_breakpoint); | ||
for (int widgetId : appWidgetIds) { | ||
Bundle extras = appWidgetManager.getAppWidgetOptions(widgetId); | ||
int widgetWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, | ||
extras.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH), displayMetrics); | ||
int widgetHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, | ||
extras.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT), displayMetrics); | ||
Bitmap image; | ||
try { | ||
BitmapFactory.Options options = new BitmapFactory.Options(); | ||
options.inJustDecodeBounds = true; | ||
BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri), null, options); | ||
int width = options.outWidth; | ||
int height = options.outHeight; | ||
options.inJustDecodeBounds = false; | ||
options.inSampleSize = Math.min(ImageUtil.calculateSampleSize(width, widgetWidth), | ||
ImageUtil.calculateSampleSize(height, widgetHeight)); | ||
image = BitmapFactory.decodeStream( | ||
contentResolver.openInputStream(imageUri), null, options); | ||
} catch (FileNotFoundException e) { | ||
Log.e(TAG, "Could not find current artwork image", e); | ||
return false; | ||
} | ||
@LayoutRes int widgetLayout = widgetHeight < smallWidgetHeight | ||
? R.layout.widget_small | ||
: R.layout.widget; | ||
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), widgetLayout); | ||
remoteViews.setContentDescription(R.id.widget_background, contentDescription); | ||
remoteViews.setImageViewBitmap(R.id.widget_background, image); | ||
remoteViews.setOnClickPendingIntent(R.id.widget_background, launchPendingIntent); | ||
remoteViews.setOnClickPendingIntent(R.id.widget_next_artwork, nextArtworkPendingIntent); | ||
if (supportsNextArtwork) { | ||
remoteViews.setViewVisibility(R.id.widget_next_artwork, View.VISIBLE); | ||
} else { | ||
remoteViews.setViewVisibility(R.id.widget_next_artwork, View.GONE); | ||
} | ||
appWidgetManager.updateAppWidget(widgetId, remoteViews); | ||
} | ||
return true; | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
main/src/main/java/com/google/android/apps/muzei/widget/MuzeiAppWidgetProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* Copyright 2014 Google Inc. | ||
* | ||
* 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.google.android.apps.muzei.widget; | ||
|
||
import android.appwidget.AppWidgetManager; | ||
import android.appwidget.AppWidgetProvider; | ||
import android.content.ComponentName; | ||
import android.content.Context; | ||
import android.content.Intent; | ||
import android.content.pm.PackageManager; | ||
import android.os.Bundle; | ||
|
||
import com.google.android.apps.muzei.SourceManager; | ||
import com.google.android.apps.muzei.api.MuzeiArtSource; | ||
|
||
/** | ||
* AppWidgetProvider for Muzei. The actual updating is done asynchronously in | ||
* {@link AppWidgetUpdateTask}. | ||
*/ | ||
public class MuzeiAppWidgetProvider extends AppWidgetProvider { | ||
static final String ACTION_NEXT_ARTWORK = "com.google.android.apps.muzei.action.WIDGET_NEXT_ARTWORK"; | ||
|
||
@Override | ||
public void onEnabled(final Context context) { | ||
// Enable the AppWidgetUpdateReceiver as we now have widgets that need to be updated | ||
PackageManager packageManager = context.getPackageManager(); | ||
ComponentName componentName = new ComponentName(context, AppWidgetUpdateReceiver.class); | ||
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, | ||
PackageManager.DONT_KILL_APP); | ||
} | ||
|
||
@Override | ||
public void onReceive(Context context, Intent intent) { | ||
super.onReceive(context, intent); | ||
if (intent != null && ACTION_NEXT_ARTWORK.equals(intent.getAction())) { | ||
SourceManager.sendAction(context.getApplicationContext(), MuzeiArtSource.BUILTIN_COMMAND_ID_NEXT_ARTWORK); | ||
} | ||
} | ||
|
||
@Override | ||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { | ||
updateWidgets(context); | ||
} | ||
|
||
|
||
@Override | ||
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, | ||
int appWidgetId, Bundle newOptions) { | ||
updateWidgets(context); | ||
} | ||
|
||
private void updateWidgets(final Context context) { | ||
final PendingResult result = goAsync(); | ||
new AppWidgetUpdateTask(context) { | ||
@Override | ||
protected void onPostExecute(Boolean success) { | ||
result.finish(); | ||
} | ||
}.execute(); | ||
} | ||
|
||
@Override | ||
public void onDisabled(final Context context) { | ||
// Disable the AppWidgetUpdateReceiver as we no longer have widgets that need to be updated | ||
PackageManager packageManager = context.getPackageManager(); | ||
ComponentName componentName = new ComponentName(context, AppWidgetUpdateReceiver.class); | ||
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, | ||
PackageManager.DONT_KILL_APP); | ||
} | ||
} |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<!-- | ||
Copyright 2014 Google Inc. | ||
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. | ||
--> | ||
|
||
<shape xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:shape="rectangle"> | ||
<gradient android:startColor="#0000" | ||
android:endColor="#4000" | ||
android:angle="270" /> | ||
</shape> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<!-- | ||
Copyright 2014 Google Inc. | ||
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. | ||
--> | ||
|
||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent"> | ||
|
||
<ImageView | ||
android:id="@+id/widget_background" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent" | ||
android:background="@android:color/black" | ||
android:scaleType="centerCrop" | ||
tools:ignore="ContentDescription" /> | ||
|
||
<FrameLayout | ||
android:layout_width="match_parent" | ||
android:layout_height="@dimen/widget_scrim_height" | ||
android:layout_gravity="bottom" | ||
android:background="@drawable/widget_scrim" /> | ||
|
||
<ImageButton | ||
android:id="@+id/widget_next_artwork" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:layout_gravity="bottom|end" | ||
android:background="@drawable/white_selectable_item_background" | ||
android:contentDescription="@string/action_next_artwork" | ||
android:padding="@dimen/widget_padding" | ||
android:src="@drawable/ic_skip" /> | ||
</FrameLayout> |
Oops, something went wrong.