/*
* Copyright (c) 2016 Ha Duy Trung
*
* 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 io.github.hidroh.materialistic.appwidget;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.annotation.StringRes;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.widget.RemoteViews;
import java.util.Locale;
import io.github.hidroh.materialistic.BestActivity;
import io.github.hidroh.materialistic.ListActivity;
import io.github.hidroh.materialistic.NewActivity;
import io.github.hidroh.materialistic.R;
import io.github.hidroh.materialistic.SearchActivity;
import io.github.hidroh.materialistic.annotation.Synthetic;
import static android.content.Context.ALARM_SERVICE;
import static android.content.Context.MODE_PRIVATE;
class WidgetHelper {
private static final String SP_NAME = "WidgetConfiguration_%1$d";
private static final int DEFAULT_FREQUENCY_HOUR = 6;
private final Context mContext;
private final AppWidgetManager mAppWidgetManager;
private final AlarmManager mAlarmManager;
WidgetHelper(Context context) {
mContext = context;
mAppWidgetManager = AppWidgetManager.getInstance(context);
mAlarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
}
void configure(int appWidgetId) {
scheduleUpdate(appWidgetId);
update(appWidgetId);
}
void update(int appWidgetId) {
WidgetConfig config = WidgetConfig.createWidgetConfig(mContext, appWidgetId);
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), config.widgetLayout);
updateTitle(remoteViews, config);
updateCollection(appWidgetId, remoteViews, config);
mAppWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
void refresh(int appWidgetId) {
mAppWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, android.R.id.list);
update(appWidgetId);
}
void remove(int appWidgetId) {
cancelScheduledUpdate(appWidgetId);
clearConfig(appWidgetId);
}
private void scheduleUpdate(int appWidgetId) {
String frequency = WidgetConfig.getConfig(mContext, appWidgetId, R.string.pref_widget_frequency);
long frequencyHourMillis = DateUtils.HOUR_IN_MILLIS * (TextUtils.isEmpty(frequency) ?
DEFAULT_FREQUENCY_HOUR : Integer.valueOf(frequency));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getJobScheduler().schedule(new JobInfo.Builder(appWidgetId,
new ComponentName(mContext.getPackageName(), WidgetRefreshJobService.class.getName()))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(frequencyHourMillis)
.build());
} else {
mAlarmManager.setInexactRepeating(AlarmManager.RTC,
System.currentTimeMillis() + frequencyHourMillis,
frequencyHourMillis,
createRefreshPendingIntent(appWidgetId));
}
}
private void cancelScheduledUpdate(int appWidgetId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getJobScheduler().cancel(appWidgetId);
} else {
mAlarmManager.cancel(createRefreshPendingIntent(appWidgetId));
}
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private JobScheduler getJobScheduler() {
return (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
private void clearConfig(int appWidgetId) {
mContext.getSharedPreferences(WidgetConfig.getConfigName(appWidgetId), MODE_PRIVATE)
.edit()
.clear()
.apply();
}
private void updateTitle(RemoteViews remoteViews, WidgetConfig config) {
remoteViews.setTextViewText(R.id.title, config.title);
remoteViews.setOnClickPendingIntent(R.id.title,
PendingIntent.getActivity(mContext, 0, config.customQuery ?
new Intent(mContext, config.destination)
.putExtra(SearchManager.QUERY, config.title) :
new Intent(mContext, config.destination), 0));
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void updateCollection(int appWidgetId, RemoteViews remoteViews, WidgetConfig config) {
remoteViews.setTextViewText(R.id.subtitle,
DateUtils.formatDateTime(mContext, System.currentTimeMillis(),
DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_TIME));
remoteViews.setOnClickPendingIntent(R.id.button_refresh,
createRefreshPendingIntent(appWidgetId));
Intent intent = new Intent(mContext, WidgetService.class)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
.putExtra(WidgetService.EXTRA_CONFIG, config.toBundle());
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
remoteViews.setRemoteAdapter(android.R.id.list, intent);
} else {
//noinspection deprecation
remoteViews.setRemoteAdapter(appWidgetId, android.R.id.list, intent);
}
remoteViews.setEmptyView(android.R.id.list, R.id.empty);
remoteViews.setPendingIntentTemplate(android.R.id.list,
PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_VIEW), 0));
}
private PendingIntent createRefreshPendingIntent(int appWidgetId) {
return PendingIntent.getBroadcast(mContext, appWidgetId,
new Intent(WidgetProvider.ACTION_REFRESH_WIDGET)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId),
PendingIntent.FLAG_UPDATE_CURRENT);
}
static class WidgetConfig {
private static final String EXTRA_CUSTOM_QUERY = "extra:customQuery";
private static final String EXTRA_TITLE = "extra:title";
private static final String EXTRA_IS_LIGHT_THEME = "extra:isLightTheme";
private static final String EXTRA_WIDGET_LAYOUT = "extra:widgetLayout";
private static final String EXTRA_SECTION = "extra:section";
boolean customQuery;
@Synthetic Class<? extends Activity> destination;
@Synthetic String title;
boolean isLightTheme;
@Synthetic @LayoutRes int widgetLayout;
String section;
@Synthetic WidgetConfig(Bundle bundle) {
customQuery = bundle.getBoolean(EXTRA_CUSTOM_QUERY);
title = bundle.getString(EXTRA_TITLE);
isLightTheme = bundle.getBoolean(EXTRA_IS_LIGHT_THEME);
widgetLayout = bundle.getInt(EXTRA_WIDGET_LAYOUT);
section = bundle.getString(EXTRA_SECTION);
destination = ListActivity.class; // not part of bundle
}
@NonNull
static WidgetConfig createWidgetConfig(Context context, int appWidgetId) {
String theme = getConfig(context, appWidgetId, R.string.pref_widget_theme),
section = getConfig(context, appWidgetId, R.string.pref_widget_section),
query = getConfig(context, appWidgetId, R.string.pref_widget_query);
WidgetConfig config = new WidgetConfig();
if (TextUtils.equals(theme, context.getString(R.string.pref_widget_theme_value_dark))) {
config.widgetLayout = R.layout.appwidget_dark;
} else if (TextUtils.equals(theme, context.getString(R.string.pref_widget_theme_value_light))) {
config.widgetLayout = R.layout.appwidget_light;
config.isLightTheme = true;
} else {
config.widgetLayout = R.layout.appwidget;
}
config.section = section;
if (!TextUtils.isEmpty(query)) {
config.title = query;
config.section = query;
config.destination = SearchActivity.class;
config.customQuery = true;
} else if (TextUtils.equals(section, context.getString(R.string.pref_widget_section_value_best))) {
config.title = context.getString(R.string.title_activity_best);
config.destination = BestActivity.class;
} else if (TextUtils.equals(section, context.getString(R.string.pref_widget_section_value_top))) {
config.title = context.getString(R.string.title_activity_list);
config.destination = ListActivity.class;
} else {
// legacy "new stories" widget
config.title = context.getString(R.string.title_activity_new);
config.destination = NewActivity.class;
}
return config;
}
@Synthetic static String getConfig(Context context, int appWidgetId, @StringRes int key) {
return context.getSharedPreferences(getConfigName(appWidgetId), MODE_PRIVATE)
.getString(context.getString(key), null);
}
static String getConfigName(int appWidgetId) {
return String.format(Locale.US, SP_NAME, appWidgetId);
}
private WidgetConfig() {}
Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putBoolean(EXTRA_CUSTOM_QUERY, customQuery);
bundle.putString(EXTRA_TITLE, title);
bundle.putBoolean(EXTRA_IS_LIGHT_THEME, isLightTheme);
bundle.putInt(EXTRA_WIDGET_LAYOUT, widgetLayout);
bundle.putString(EXTRA_SECTION, section);
return bundle;
}
}
}