com.github.michalbednarski.intentslab.ReceiveBroadcastService.java Source code

Java tutorial

Introduction

Here is the source code for com.github.michalbednarski.intentslab.ReceiveBroadcastService.java

Source

/*
 * IntentsLab - Android app for playing with Intents and Binder IPC
 * Copyright (C) 2014 Micha Bednarski
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.github.michalbednarski.intentslab;

import android.annotation.TargetApi;
import android.app.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.github.michalbednarski.intentslab.editor.IntentEditorActivity;
import com.github.michalbednarski.intentslab.editor.IntentEditorConstants;

import java.util.ArrayList;

public class ReceiveBroadcastService extends Service {
    private static final String TAG = "ReceiveBroadcast";
    private static final int SERVICE_NOTIFICATION_ID = 1;
    private static final int RESULT_NOTIFICATION_ID = 2;

    static boolean sIsRunning = false;
    private MyBroadcastReceiver mReceiver = null;
    private boolean mGotBroadcast = false;

    private static class ReceivedBroadcast {
        long time;
        Intent intent;
        String description = "";
    };

    private static ArrayList<ReceivedBroadcast> sReceivedBroadcasts = null;

    public static void startReceiving(Context context, IntentFilter[] filters, boolean multiple) {
        if (!multiple) {
            for (IntentFilter filter : filters) {
                Intent stickyBroadcastIntent = context.registerReceiver(null, filter);
                if (stickyBroadcastIntent != null) {
                    context.startActivity(new Intent(context, IntentEditorActivity.class)
                            .putExtra(IntentEditorActivity.EXTRA_INTENT, stickyBroadcastIntent)
                            .putExtra(IntentEditorActivity.EXTRA_COMPONENT_TYPE, IntentEditorConstants.BROADCAST));
                    Toast.makeText(context, R.string.received_sticky_broadcast, Toast.LENGTH_SHORT).show();
                    return;
                }
            }
        }

        context.startService(new Intent(context, ReceiveBroadcastService.class).putExtra("intentFilters", filters)
                .putExtra("multiple", multiple));
    }

    public static void startReceiving(Context context, String action, boolean multiple) {
        IntentFilter[] filters = { new IntentFilter(action) };
        startReceiving(context, filters, multiple);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Flag us as running
        sIsRunning = true;

        // Prepare receiver and unregister old one if exist
        if (mReceiver != null) {
            // We were already started, clear old receiver
            unregisterReceiver(mReceiver);
        } else {
            mReceiver = new MyBroadcastReceiver();
        }

        if (intent.getBooleanExtra("multiple", false)) {
            sReceivedBroadcasts = new ArrayList<ReceivedBroadcast>();
        } else {
            sReceivedBroadcasts = null;
        }

        // Get IntentFilter and register receiver
        String action = "";
        Parcelable[] filters = intent.getParcelableArrayExtra("intentFilters");
        if (filters == null || filters.length == 0) {
            stopSelf();
            return START_NOT_STICKY;
        }
        for (Parcelable uncastedFilter : filters) {
            IntentFilter filter = (IntentFilter) uncastedFilter;
            registerReceiver(mReceiver, filter);
            if (filters.length == 1) {
                if (filter.countActions() == 1) {
                    action = filter.getAction(0);
                }
            }
        }

        // Show notification
        if (sReceivedBroadcasts != null) {
            showListeningMultipleNotification();
        } else {
            showWaitingNotification(action);
        }
        return START_NOT_STICKY;
    }

    private boolean isAutoEditEnabled() {
        return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("autoeditbroadcast", false);
    }

    private NotificationManager getNotificationManager() {
        return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    void showWaitingNotification(String action) {
        mGotBroadcast = false;

        Intent requeryAction = new Intent(this, ReceiveBroadcastDialog.WrapperActivity.class);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, requeryAction, 0);

        String title = getResources().getString(R.string.waiting_for_broadcast);

        final Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_action_send).setOngoing(true).setTicker(title).setContentTitle(title)
                .setContentText(action).setContentIntent(contentIntent).build();
        startForeground(SERVICE_NOTIFICATION_ID, notification);
    }

    void showCaughtNotification(Intent receivedBroadcast) {
        mGotBroadcast = true;

        Intent runEditor = new Intent(this, IntentEditorActivity.class);
        runEditor.putExtra(IntentEditorActivity.EXTRA_INTENT, receivedBroadcast);
        runEditor.putExtra(IntentEditorActivity.EXTRA_COMPONENT_TYPE, IntentEditorConstants.BROADCAST);
        runEditor.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, runEditor,
                PendingIntent.FLAG_UPDATE_CURRENT);

        if (isAutoEditEnabled()) {
            startActivity(runEditor);
        } else {
            final String title = getResources().getString(R.string.got_broadcast);
            final String action = receivedBroadcast.getAction();

            final Notification notification = new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ic_action_send).setAutoCancel(true).setTicker(title)
                    .setContentTitle(title).setContentText(action).setContentIntent(contentIntent).build();

            getNotificationManager().notify(RESULT_NOTIFICATION_ID, notification);
        }
    }

    void showListeningMultipleNotification() {
        mGotBroadcast = true;

        Intent viewBroadcastsListIntent = new Intent(this, BroadcastsListActivity.class);
        viewBroadcastsListIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, viewBroadcastsListIntent, 0);

        String title = getString(R.string.listening_for_multiple_broadcasts);
        int receivedSoFar = sReceivedBroadcasts.size();
        String message = receivedSoFar == 0 ? getString(R.string.nothing_received_so_far)
                : getResources().getQuantityString(R.plurals.n_broadcasts_received_so_far, receivedSoFar,
                        receivedSoFar);

        final Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_action_send).setOngoing(true).setTicker(title).setContentTitle(title)
                .setContentText(message).setContentIntent(contentIntent).build();

        startForeground(SERVICE_NOTIFICATION_ID, notification);
    }

    @Override
    public void onDestroy() {
        sIsRunning = false;
        unregisterReceiver(mReceiver);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (sReceivedBroadcasts != null) {
                // Running in receive multiple mode, add broadcast to list
                ReceivedBroadcast receivedBroadcast = new ReceivedBroadcast();
                receivedBroadcast.time = System.currentTimeMillis();
                receivedBroadcast.intent = intent;

                // Find last broadcast with same action
                Intent previousBroadcastIntent = null;
                ReceivedBroadcast previousBroadcast = null;
                for (int i = sReceivedBroadcasts.size() - 1; i >= 0; i--) {
                    previousBroadcast = sReceivedBroadcasts.get(i);
                    previousBroadcastIntent = previousBroadcast.intent;
                    if (previousBroadcastIntent.getAction().equals(intent.getAction())) {
                        break;
                    } else if (i == 0) {
                        previousBroadcastIntent = null;
                    }
                }

                // Generate description
                String description = "";
                if (previousBroadcastIntent == null) {
                    if (isInitialStickyBroadcast()) {
                        description = context.getString(R.string.initial_sticky);
                    }
                } else {

                    if (intent.getData() != null) {
                        description = intent.getDataString();
                    } else {
                        description = context.getString(R.string.s_after_previous_broadcast,
                                (receivedBroadcast.time - previousBroadcast.time) / 1000);
                    }

                    if (previousBroadcastIntent.getFlags() != intent.getFlags()) {
                        description += "\n" + context.getString(R.string.flags_changed);
                    }

                    // Extras changes
                    Bundle extras = intent.getExtras();
                    Bundle previousExtras = previousBroadcastIntent.getExtras();
                    if (extras == null || extras.size() == 0) {
                        receivedBroadcast.description += "\n" + context.getString(R.string.no_extras);
                    } else if (previousExtras != null && previousExtras.size() != 0) {
                        // Both have extras, compare them
                        for (String extraName : extras.keySet()) {
                            Object oldValue = previousExtras.get(extraName);
                            Object newValue = extras.get(extraName);
                            if (oldValue == null) {
                                description += "\n" + getString(R.string.added_extra, extraName);
                            } else if (Utils.hasOverriddenEqualsMethod(newValue) && !newValue.equals(oldValue)) {
                                description += "\n" + extraName + ": " + oldValue + " -> " + newValue;
                            }
                            if (description.length() > 500) {
                                description += "\n[...]";
                                break;
                            }
                        }
                    }
                }
                receivedBroadcast.description = description;

                sReceivedBroadcasts.add(receivedBroadcast);
                for (BroadcastsListActivity listActivity : sListActivities) {
                    listActivity.mAdapter.notifyDataSetChanged();
                }
                showListeningMultipleNotification();
            } else {
                showCaughtNotification(intent);
                stopSelf(); // Stop my service, unregister receiver
            }
        }
    }

    // Activity for viewing multiple broadcasts
    private static ArrayList<BroadcastsListActivity> sListActivities = new ArrayList<BroadcastsListActivity>();

    public static class BroadcastsListActivity extends ListActivity implements AdapterView.OnItemClickListener {
        ArrayAdapter<ReceivedBroadcast> mAdapter;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            if (sReceivedBroadcasts == null) {
                Log.e(TAG, "Unexpected start of BroadcastsListActivity");
                finish();
                return;
            }

            mAdapter = new ArrayAdapter<ReceivedBroadcast>(this, 0, sReceivedBroadcasts) {
                @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                    if (convertView == null) {
                        convertView = getLayoutInflater().inflate(android.R.layout.simple_list_item_2, parent,
                                false);
                    }
                    ReceivedBroadcast receivedBroadcast = mAdapter.getItem(position);
                    ((TextView) convertView.findViewById(android.R.id.text1))
                            .setText(Utils.afterLastDot(receivedBroadcast.intent.getAction()));
                    ((TextView) convertView.findViewById(android.R.id.text2))
                            .setText(receivedBroadcast.description);
                    return convertView;
                }
            };
            setListAdapter(mAdapter);
            getListView().setOnItemClickListener(this);
            sListActivities.add(this);
        }

        @Override
        protected void onDestroy() {
            sListActivities.remove(this);
            super.onDestroy();
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            startActivity(new Intent(this, IntentEditorActivity.class)
                    .putExtra(IntentEditorActivity.EXTRA_COMPONENT_TYPE, IntentEditorConstants.BROADCAST)
                    .putExtra(IntentEditorActivity.EXTRA_INTENT, mAdapter.getItem(position).intent));
        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.received_broadcasts, menu);
            return true;
        }

        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
            menu.findItem(R.id.stop_listening_for_broadcasts).setVisible(sIsRunning).setEnabled(sIsRunning);
            return true;
        }

        @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            if (item.getItemId() == R.id.stop_listening_for_broadcasts) {
                stopService(new Intent(this, ReceiveBroadcastService.class));
                sIsRunning = false;
                try {
                    invalidateOptionsMenu();
                } catch (NoSuchMethodError ignored) {
                }
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
}