Android widget base

This commit is contained in:
Andrei 2020-04-03 08:44:47 -07:00
parent 48c6aa6dea
commit 0d52d6e710
11 changed files with 135 additions and 138 deletions

View file

@ -137,7 +137,7 @@
<activity
android:name=".activity.SocialFeedReading"/>
<activity android:name=".widget.ConfigureWidgetActivity">
<activity android:name=".widget.WidgetConfigActivity">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
@ -145,9 +145,8 @@
<service
android:name=".service.NBSyncService"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".widget.BlurWidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS"
/>
<service android:name=".widget.WidgetRemoteViewsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver android:name=".service.BootReceiver">
<intent-filter>
@ -158,7 +157,7 @@
<receiver android:name=".util.NotifyDismissReceiver" android:exported="false" />
<receiver android:name=".util.NotifySaveReceiver" android:exported="false" />
<receiver android:name=".util.NotifyMarkreadReceiver" android:exported="false" />
<receiver android:name=".widget.NewsBlurWidgetProvider" >
<receiver android:name=".widget.WidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/half_black" />
<corners
android:bottomLeftRadius="4dp"
android:bottomRightRadius="4dp"
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
</shape>

View file

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<LinearLayout
android:background="@color/white"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_feed_name"
style="@style/rowItemHeaderBackground"
tools:text="Coding Horror"
android:paddingTop="9dp"
android:paddingBottom="9dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:shadowDy="1"
android:textStyle="bold"
android:lines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ListView
tools:listitem="@layout/newsblur_widget_item"
android:id="@+id/widget_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- Note that empty views must be siblings of the collection view
for which the empty view represents empty state.-->
</FrameLayout>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:background="@drawable/app_widget_background"
android:clipChildren="false">
<TextView
android:id="@+id/txt_feed_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="1"
android:paddingStart="8dp"
android:paddingTop="9dp"
android:paddingEnd="8dp"
android:paddingBottom="9dp"
android:textColor="@color/white"
android:textStyle="bold"
tools:text="Coding Horror" />
<ListView
android:id="@+id/widget_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="46dp"
tools:listitem="@layout/view_widget_item" />
<!-- Note that empty views must be siblings of the collection view
for which the empty view represents empty state.-->
</FrameLayout>

View file

@ -1,31 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/newsblur_widget_item"
android:orientation="horizontal"
android:gravity="center_vertical"
android:id="@+id/view_widget_item"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/widget_item_title"
android:paddingBottom="7dp"
android:paddingTop="7dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:ellipsize="end"
tools:text="The Next CEO of Stackoverflow"
android:lines="1"
android:textSize="14sp"
android:textStyle="bold"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"/>
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/widget_item_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:lines="1"
android:textColor="@color/white"
android:paddingStart="8dp"
android:paddingTop="7dp"
android:paddingEnd="8dp"
android:paddingBottom="7dp"
android:textSize="14sp"
android:textStyle="bold"
tools:text="The Next CEO of Stackoverflow" />
<TextView
android:textColor="@color/white"
android:id="@+id/widget_item_time"
tools:text="30 min ago"
android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:paddingEnd="8dp"
android:textSize="12sp"
tools:text="30 min ago" />
</LinearLayout>

View file

@ -5,8 +5,8 @@
android:minResizeWidth="100dp"
android:minResizeHeight="60dp"
android:updatePeriodMillis="14400000"
android:initialLayout="@layout/newsblur_widget"
android:configure="com.newsblur.widget.ConfigureWidgetActivity"
android:initialLayout="@layout/view_app_widget"
android:configure="com.newsblur.widget.WidgetConfigActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
</appwidget-provider>

View file

@ -39,7 +39,7 @@ import com.newsblur.util.ReadingAction;
import com.newsblur.util.ReadFilter;
import com.newsblur.util.StateFilter;
import com.newsblur.util.StoryOrder;
import com.newsblur.widget.NewsBlurWidgetProvider;
import com.newsblur.widget.WidgetProvider;
import java.util.ArrayList;
import java.util.Date;
@ -47,7 +47,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
@ -285,7 +284,7 @@ public class NBSyncService extends JobService {
housekeeping();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, NewsBlurWidgetProvider.class));
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(this, WidgetProvider.class));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
// check to see if we are on an allowable network only after ensuring we have CPU

View file

@ -17,7 +17,6 @@ import com.newsblur.R;
import com.newsblur.activity.NbActivity;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.network.APIManager;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.Log;
import com.newsblur.util.PrefsUtils;
@ -27,12 +26,13 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ConfigureWidgetActivity extends NbActivity {
public class WidgetConfigActivity extends NbActivity {
private static String TAG = "WidgetConfigActivity";
private int appWidgetId;
private List<Feed> feeds = new ArrayList<>();
private List<Folder> folders = new ArrayList<>();
private static String TAG = "ConfigureWidgetActivity";
private Feed selectedFeed = null;
private Folder selectedFolder = null;
@ -149,9 +149,9 @@ public class ConfigureWidgetActivity extends NbActivity {
//update widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
RemoteViews rv = new RemoteViews(getPackageName(),
R.layout.newsblur_widget);
R.layout.view_app_widget);
Intent intent = new Intent(this, BlurWidgetRemoteViewsService.class);
Intent intent = new Intent(this, WidgetRemoteViewsService.class);
// Add the app widget ID to the intent extras.
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
@ -170,11 +170,11 @@ public class ConfigureWidgetActivity extends NbActivity {
rv.setEmptyView(R.id.widget_list, R.id.empty_view);
Intent touchIntent = new Intent(this, NewsBlurWidgetProvider.class);
Intent touchIntent = new Intent(this, WidgetProvider.class);
// Set the action for the intent.
// When the user touches a particular view, it will have the effect of
// broadcasting TOAST_ACTION.
touchIntent.setAction(NewsBlurWidgetProvider.ACTION_OPEN_STORY);
touchIntent.setAction(WidgetProvider.ACTION_OPEN_STORY);
touchIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent touchIntentTemplate = PendingIntent.getBroadcast(this, 0, touchIntent,
@ -182,7 +182,6 @@ public class ConfigureWidgetActivity extends NbActivity {
rv.setPendingIntentTemplate(R.id.widget_list, touchIntentTemplate);
appWidgetManager.updateAppWidget(appWidgetId, rv);
Intent resultValue = new Intent();

View file

@ -7,24 +7,22 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.RemoteViews;
import android.widget.Toast;
import com.newsblur.R;
import com.newsblur.activity.FeedReading;
import com.newsblur.activity.Reading;
import com.newsblur.database.BlurDatabaseHelper;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.Log;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.UIUtils;
public class NewsBlurWidgetProvider extends AppWidgetProvider {
public class WidgetProvider extends AppWidgetProvider {
public static String ACTION_OPEN_STORY = "ACTION_OPEN_STORY";
public static String EXTRA_ITEM_ID = "EXTRA_ITEM_ID";
public static String EXTRA_FEED_ID = "EXTRA_FEED_ID";
private static String TAG = "NewsBlurWidgetProvider";
private static String TAG = "WidgetProvider";
// Called when the BroadcastReceiver receives an Intent broadcast.
// Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget
// displays a Toast message for the current item.
@ -47,8 +45,9 @@ public class NewsBlurWidgetProvider extends AppWidgetProvider {
}
super.onReceive(context, intent);
}
/**
* Called to update at regular interval
* This is called to update the App Widget at intervals defined by the updatePeriodMillis attribute
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
@ -57,16 +56,15 @@ public class NewsBlurWidgetProvider extends AppWidgetProvider {
for (int i = 0; i < appWidgetIds.length; ++i) {
Log.d(TAG, "onUpdate iteration #" + i);
// Set up the intent that starts the BlurWidgetRemoteViewService, which will
// Set up the intent that starts the WidgetRemoteViewService, which will
// provide the views for this collection.
Intent intent = new Intent(context, BlurWidgetRemoteViewsService.class);
Intent intent = new Intent(context, WidgetRemoteViewsService.class);
// Add the app widget ID to the intent extras.
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
// Instantiate the RemoteViews object for the app widget layout.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.newsblur_widget);
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.view_app_widget);
// Set up the RemoteViews object to use a RemoteViews adapter.
// This adapter connects
// to a RemoteViewsService through the specified intent.
@ -76,7 +74,8 @@ public class NewsBlurWidgetProvider extends AppWidgetProvider {
// The empty view is displayed when the collection has no items.
// It should be in the same layout used to instantiate the RemoteViews
// object above.
rv.setEmptyView(R.id.widget_list, R.id.empty_view);
//TODO: create and show empty view if/when needed
// rv.setEmptyView(R.id.widget_list, R.id.empty_view);
rv.setTextViewText(R.id.txt_feed_name,
PrefsUtils.getWidgetFeedName(context, appWidgetIds[i]));
@ -89,24 +88,19 @@ public class NewsBlurWidgetProvider extends AppWidgetProvider {
// cannot set up their own pending intents. Instead, the collection as a whole sets
// up a pending intent template, and the individual items set a fillInIntent
// to create unique behavior on an item-by-item basis.
Intent touchIntent = new Intent(context, NewsBlurWidgetProvider.class);
Intent touchIntent = new Intent(context, WidgetProvider.class);
// Set the action for the intent.
// When the user touches a particular view, it will have the effect of
// broadcasting TOAST_ACTION.
touchIntent.setAction(NewsBlurWidgetProvider.ACTION_OPEN_STORY);
// broadcasting ACTION_OPEN_STORY.
touchIntent.setAction(WidgetProvider.ACTION_OPEN_STORY);
touchIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent touchIntentTemplate = PendingIntent.getBroadcast(context, 0, touchIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
rv.setPendingIntentTemplate(R.id.widget_list, touchIntentTemplate);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
}

View file

@ -4,8 +4,6 @@ import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
@ -21,27 +19,20 @@ import com.newsblur.util.ReadFilter;
import com.newsblur.util.StoryOrder;
import com.newsblur.util.StoryUtils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BlurWidgetRemoteViewsService extends RemoteViewsService {
private static String TAG = "BlurWidgetRemoteViewsFactory";
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
Log.d(TAG, "onGetViewFactory");
return new BlurWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
}
}
public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private static String TAG = "WidgetRemoteViewsFactory";
class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context context;
private static String TAG = "BlurWidgetRemoteViewsFactory";
private List<Story> storyItems = new ArrayList<Story>();
private List<Story> storyItems = new ArrayList<>();
private FeedSet fs;
private APIManager apiManager;
public BlurWidgetRemoteViewsFactory(Context context, Intent intent) {
public WidgetRemoteViewsFactory(Context context, Intent intent) {
Log.d(TAG, "Constructor");
this.context = context;
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
@ -52,19 +43,19 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
apiManager = new APIManager(context);
Log.d(TAG, "Feed ID: " + feedId);
if(feedId != null){
if (feedId != null) {
// this is a single feed
fs = FeedSet.singleFeed(feedId);
}else{
} else {
// this is a folder
fs = FeedUtils.feedSetFromFolderName(feedName);
}
}
/**
* The system calls onCreate() when creating your factory for the first time.
* This is where you set up any connections and/or cursors to your data source.
*
* <p>
* Heavy lifting,
* for example downloading or creating content etc, should be deferred to onDataSetChanged()
* or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
@ -93,35 +84,30 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
storyItems.clear();
storyItems.addAll(Arrays.asList(response.stories));
}
/**
* allowed to run synchronous calls
* Allowed to run synchronous calls
*/
@Override
public RemoteViews getViewAt(int position) {
Log.d(TAG, "getViewAt " + position);
Story story = storyItems.get(position);
// Construct a remote views item based on the app widget item XML file,
// and set the text based on the position.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.newsblur_widget_item);
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.view_widget_item);
rv.setTextViewText(R.id.widget_item_title, story.title);
CharSequence time = StoryUtils.formatRelativeTime(context, story.timestamp);
rv.setTextViewText(R.id.widget_item_time, time);
// Next, set a fill-intent, which will be used to fill in the pending intent template
// that is set on the collection view in StackWidgetProvider.
// set fill-intent which is used to fill in the pending intent template
// set on the collection view in WidgetProvider
Bundle extras = new Bundle();
extras.putString(NewsBlurWidgetProvider.EXTRA_ITEM_ID, story.storyHash);
extras.putString(NewsBlurWidgetProvider.EXTRA_FEED_ID, story.feedId);
extras.putString(WidgetProvider.EXTRA_ITEM_ID, story.storyHash);
extras.putString(WidgetProvider.EXTRA_FEED_ID, story.feedId);
Intent fillInIntent = new Intent();
// fillInIntent.setAction(NewsBlurWidgetProvider.ACTION_OPEN_STORY);
fillInIntent.putExtras(extras);
// Make it possible to distinguish the individual on-click
// action of a given item
rv.setOnClickFillInIntent(R.id.newsblur_widget_item, fillInIntent);
rv.setOnClickFillInIntent(R.id.view_widget_item, fillInIntent);
return rv;
}
@ -138,7 +124,6 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
}
/**
*
* @return The number of types of Views that will be returned by this factory.
*/
@Override
@ -147,7 +132,6 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
}
/**
*
* @param position The position of the item within the data set whose row id we want.
* @return The id of the item at the specified position.
*/
@ -157,7 +141,6 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
}
/**
*
* @return True if the same id always refers to the same object.
*/
@Override
@ -168,9 +151,8 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
@Override
public void onDataSetChanged() {
// fetch any new data
fetchStories();
Log.d(TAG, "onDataSetChanged");
fetchStories();
}
/**
@ -183,7 +165,6 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
}
/**
*
* @return Count of items.
*/
@Override
@ -191,4 +172,4 @@ class BlurWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFact
Log.d(TAG, "getCount: " + Math.min(storyItems.size(), 10));
return Math.min(storyItems.size(), 10);
}
}
}

View file

@ -0,0 +1,17 @@
package com.newsblur.widget;
import android.content.Intent;
import android.widget.RemoteViewsService;
import com.newsblur.util.Log;
public class WidgetRemoteViewsService extends RemoteViewsService {
private static String TAG = "WidgetRemoteViewsFactory";
@Override
public RemoteViewsService.RemoteViewsFactory onGetViewFactory(Intent intent) {
Log.d(TAG, "onGetViewFactory");
return new WidgetRemoteViewsFactory(this.getApplicationContext(), intent);
}
}