/* This file is part of ZAX. ZAX is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ZAX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ZAX. If not, see <http://www.gnu.org/licenses/>. */ package com.inovex.zabbixmobile.data; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Intent; import android.os.AsyncTask; import android.os.Build; import android.os.IBinder; import android.util.Log; import android.view.View; import android.widget.RemoteViews; import android.widget.Toast; import com.inovex.zabbixmobile.R; import com.inovex.zabbixmobile.activities.ProblemsActivity; import com.inovex.zabbixmobile.exceptions.FatalException; import com.inovex.zabbixmobile.exceptions.ZabbixLoginRequiredException; import com.inovex.zabbixmobile.model.Cache.CacheDataType; import com.inovex.zabbixmobile.model.HostGroup; import com.inovex.zabbixmobile.model.Trigger; import com.inovex.zabbixmobile.model.TriggerSeverity; import com.inovex.zabbixmobile.model.ZaxPreferences; import com.inovex.zabbixmobile.widget.ZaxWidgetProvider; import com.inovex.zabbixmobile.widget.ZaxWidgetProviderList; import com.j256.ormlite.android.apptools.OpenHelperManager; import java.util.ArrayList; import java.util.List; /** * Started service providing the homescreen widget with functionality to * retrieve data from Zabbix (at the moment the active triggers). * */ public class HomescreenWidgetService extends Service { public static final String WIDGET_ID = "WIDGET_ID"; private enum DisplayStatus { ZAX_ERROR(R.drawable.severity_high), OK(R.drawable.ok), AVG(R.drawable.severity_average), HIGH(R.drawable.severity_high), LOADING(R.drawable.icon); private final int drawable; DisplayStatus(int drawable) { this.drawable = drawable; } int getDrawable() { return drawable; } } public static final String UPDATE = "update"; private static final String TAG = HomescreenWidgetService.class .getSimpleName(); private DatabaseHelper mDatabaseHelper; private RemoteAPITask importProblemsTask; @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.d(TAG, "onStart"); /** * Given Intent can be null * http://developer.android.com/reference/android/app/Service.html#onStart%28android.content.Intent,%20int%29 */ if(intent == null) { Log.d(TAG, "intent is null, stopSelf will be called"); stopSelf(); return; } final int widgetId = intent.getIntExtra(WIDGET_ID, -1); if (widgetId == -1) { stopSelf(); return; } long zabbixServerId = ZaxPreferences.getInstance(getApplicationContext()).getWidgetServer(widgetId); if (zabbixServerId == -1) { // migrate from old version zabbixServerId = ZaxPreferences.getInstance(getApplicationContext()).getServerSelection(); ZaxPreferences.getInstance(getApplicationContext()).setWidgetServer(widgetId, zabbixServerId); } updateView(null, getResources().getString(R.string.widget_loading), true, widgetId); if (zabbixServerId == -1) { updateView( DisplayStatus.ZAX_ERROR.getDrawable(), getResources().getString( R.string.widget_connection_error), false, widgetId); stopSelf(); return; } if (mDatabaseHelper == null) { // set up SQLite connection using OrmLite mDatabaseHelper = OpenHelperManager.getHelper(this, DatabaseHelper.class); } final ZabbixRemoteAPI mRemoteAPI = new ZabbixRemoteAPI(this.getApplicationContext(), mDatabaseHelper, zabbixServerId, null); if (importProblemsTask != null && importProblemsTask.getStatus() == AsyncTask.Status.RUNNING && Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { // your android version is too old Toast.makeText(getApplicationContext(), R.string.android_version_too_old_for_parallel, Toast.LENGTH_LONG).show(); stopSelf(); return; } importProblemsTask = new RemoteAPITask(mRemoteAPI,this) { private List<Trigger> problems; private boolean error; @Override protected void executeTask() throws ZabbixLoginRequiredException, FatalException { problems = new ArrayList<Trigger>(); try { if (!mRemoteAPI.isLoggedIn()) { mRemoteAPI.authenticate(); } // ensure that hosts and host groups are available; if they // are cached, no API call will be triggered. mRemoteAPI.importHostsAndGroups(); mDatabaseHelper.clearTriggers(); // we need to refresh triggers AND events because otherwise // we might lose triggers belonging to events. But we do not // want to do that here, but set them to "not cached" so // they will be refreshed on demand mDatabaseHelper.setNotCached(CacheDataType.EVENT, null); mDatabaseHelper.setNotCached(CacheDataType.TRIGGER, null); mRemoteAPI.importActiveTriggers(null); } catch (Exception e) { error = true; e.printStackTrace(); return; } finally { problems = mDatabaseHelper .getProblemsBySeverityAndHostGroupId( TriggerSeverity.ALL, HostGroup.GROUP_ID_ALL); } } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); if (error) { updateView( DisplayStatus.ZAX_ERROR.getDrawable(), getResources().getString( R.string.widget_connection_error), false, widgetId); importProblemsTask = null; stopSelf(); return; } if (problems != null) { // for (Trigger t : problems) // Log.d(TAG, t.toString()); String status; int icon = R.drawable.ok; if (problems.size() > 0) { int countHigh = 0; int countAverage = 0; for (Trigger trigger : problems) { // make sure disabled triggers are not ignored if (trigger.getStatus() != Trigger.STATUS_ENABLED) continue; if (trigger.getPriority() == TriggerSeverity.DISASTER || trigger.getPriority() == TriggerSeverity.HIGH) countHigh++; else countAverage++; } if (countHigh > 0) icon = DisplayStatus.HIGH.getDrawable(); else if (countAverage > 0) icon = DisplayStatus.AVG.getDrawable(); else icon = DisplayStatus.OK.getDrawable(); status = problems.get(0).getDescription(); status = getResources().getString( R.string.widget_problems, countHigh, countAverage); } else { icon = DisplayStatus.OK.getDrawable(); status = getResources().getString(R.string.ok); } updateView(icon, status, false, widgetId); importProblemsTask = null; stopSelf(); } } }; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { importProblemsTask.execute(); } else { executeTask(); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void executeTask() { importProblemsTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); } @Override public void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); } @SuppressLint("NewApi") private void updateView(Integer statusIcon, String statusText, boolean startProgressSpinner, int widgetId) { AppWidgetManager appWidgetManager = AppWidgetManager .getInstance(getApplicationContext()); ComponentName widget1x1 = new ComponentName(getApplicationContext(), ZaxWidgetProvider.class); ComponentName widgetList = new ComponentName(getApplicationContext(), ZaxWidgetProviderList.class); List<Integer> widgetIds1x1 = new ArrayList<Integer>(); for (int id : appWidgetManager.getAppWidgetIds(widget1x1)) { widgetIds1x1.add(id); } List<Integer> widgetIdsList = new ArrayList<Integer>(); for (int id : appWidgetManager.getAppWidgetIds(widgetList)) { widgetIdsList.add(id); } Log.d(TAG, "widget IDs 1x1: " + widgetIds1x1); Log.d(TAG, "widget IDs list: " + widgetIdsList); if (widgetIds1x1.size() <= 0 && widgetIdsList.size() <= 0) { ZaxWidgetProvider.stopAlarm(getApplicationContext()); Log.d(TAG, "no widgets added -> stopping alarm"); } if (widgetIds1x1.contains(widgetId)) { Log.d(TAG, "updating widget. ID: " + widgetId + ", Provider: " + appWidgetManager.getAppWidgetInfo(widgetId).provider.toString()); RemoteViews remoteViews = new RemoteViews(getApplicationContext() .getPackageName(), R.layout.homescreen_widget_1x1); if (startProgressSpinner) { remoteViews.setViewVisibility(R.id.refresh_button, View.GONE); remoteViews.setViewVisibility(R.id.refresh_progress, View.VISIBLE); } else { remoteViews .setViewVisibility(R.id.refresh_button, View.VISIBLE); remoteViews.setViewVisibility(R.id.refresh_progress, View.GONE); } // Set the text remoteViews.setTextViewText(R.id.content, statusText); // set icon if (statusIcon != null) remoteViews.setImageViewResource(R.id.status_image, statusIcon); // widget click Intent statusButtonClickIntent = new Intent( this.getApplicationContext(), ProblemsActivity.class); statusButtonClickIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); statusButtonClickIntent.setAction(Intent.ACTION_MAIN); statusButtonClickIntent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent pendingIntent = PendingIntent.getActivity( getApplicationContext(), 123456, statusButtonClickIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setOnClickPendingIntent(R.id.widget, pendingIntent); // refresh click Intent refreshClickIntent = new Intent( this.getApplicationContext(), ZaxWidgetProvider.class); refreshClickIntent .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int[] ids = { widgetId }; refreshClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); PendingIntent pendingIntent2 = PendingIntent.getBroadcast( getApplicationContext(), widgetId, refreshClickIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setOnClickPendingIntent(R.id.refresh_button, pendingIntent2); appWidgetManager.updateAppWidget(widgetId, remoteViews); } if (widgetIdsList.contains(widgetId)) { Log.d(TAG, "updating widget. ID: " + widgetId + ", Provider: " + appWidgetManager.getAppWidgetInfo(widgetId).provider.toString()); // appWidgetManager.notifyAppWidgetViewDataChanged(id, // R.id.list_view); RemoteViews remoteViews = new RemoteViews(getApplicationContext() .getPackageName(), R.layout.homescreen_widget_list); if (startProgressSpinner) { remoteViews.setViewVisibility(R.id.refresh_button, View.GONE); remoteViews.setViewVisibility(R.id.refresh_progress, View.VISIBLE); } else { remoteViews .setViewVisibility(R.id.refresh_button, View.VISIBLE); remoteViews.setViewVisibility(R.id.refresh_progress, View.GONE); } // widget click Intent statusButtonClickIntent = new Intent( this.getApplicationContext(), ProblemsActivity.class); statusButtonClickIntent.setAction(Intent.ACTION_MAIN); statusButtonClickIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); statusButtonClickIntent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent pendingIntent = PendingIntent.getActivity( getApplicationContext(), 12345, statusButtonClickIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setOnClickPendingIntent(R.id.widget, pendingIntent); // refresh click Intent refreshClickIntent = new Intent( this.getApplicationContext(), ZaxWidgetProvider.class); refreshClickIntent .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int[] ids = { widgetId }; refreshClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); PendingIntent pendingIntent2 = PendingIntent.getBroadcast( getApplicationContext(), widgetId, refreshClickIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setOnClickPendingIntent(R.id.refresh_button, pendingIntent2); // set up item click intents Intent itemIntent = new Intent(getApplicationContext(), ProblemsActivity.class); PendingIntent itemPendingIntent = PendingIntent.getActivity( getApplicationContext(), 0, itemIntent, PendingIntent.FLAG_CANCEL_CURRENT); remoteViews.setPendingIntentTemplate(R.id.list_view, itemPendingIntent); boolean isError = statusText.equals(getResources().getString( R.string.widget_connection_error)); if (isError) { remoteViews.setViewVisibility(R.id.list_view, View.GONE); remoteViews.setViewVisibility(R.id.error_view, View.VISIBLE); } else if (!startProgressSpinner) { remoteViews.setViewVisibility(R.id.list_view, View.VISIBLE); remoteViews.setViewVisibility(R.id.error_view, View.GONE); // empty view remoteViews.setEmptyView(R.id.list_view, R.id.empty_view); // fill list Intent intent = new Intent(getApplicationContext(), HomescreenCollectionWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); remoteViews.setRemoteAdapter(widgetId, R.id.list_view, intent); appWidgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.list_view); } appWidgetManager.updateAppWidget(widgetId, remoteViews); } } @Override public IBinder onBind(Intent intent) { return null; } }