org.sevenleaves.droidsensor.DroidSensorService.java Source code

Java tutorial

Introduction

Here is the source code for org.sevenleaves.droidsensor.DroidSensorService.java

Source

/*
 * Copyright (C) 2009, DroidSensor - http://code.google.com/p/droidsensor/
 *
 * 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 org.sevenleaves.droidsensor;

import static org.sevenleaves.droidsensor.ServiceUtils.callLater;
import static org.sevenleaves.droidsensor.ServiceUtils.cancelImmediatly;
import static org.sevenleaves.droidsensor.ServiceUtils.isActionContinue;
import static org.sevenleaves.droidsensor.ServiceUtils.isStopAction;

import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.sevenleaves.droidsensor.DatabaseManipulation.ManipulationScope;
import org.sevenleaves.droidsensor.bluetooth.BluetoothDeviceStub;
import org.sevenleaves.droidsensor.bluetooth.BluetoothDeviceStubFactory;
import org.sevenleaves.droidsensor.bluetooth.BluetoothSettings;
import org.sevenleaves.droidsensor.bluetooth.BluetoothUtils;
import org.sevenleaves.droidsensor.handlers.BluetoothEventControllerImpl;
import org.sevenleaves.droidsensor.handlers.BluetoothState;
import org.sevenleaves.droidsensor.handlers.BluetoothStateListenerAdapter;
import org.sevenleaves.droidsensor.handlers.DiscoveryCompletedHandler;
import org.sevenleaves.droidsensor.handlers.DiscoveryStartedHandler;
import org.sevenleaves.droidsensor.handlers.ScanModeConnectableDiscoverableHandler;
import org.sevenleaves.droidsensor.handlers.ScanModeConnectableHandler;
import org.sevenleaves.droidsensor.handlers.ScanModeNoneHandler;
import org.sevenleaves.droidsensor.handlers.StateOffHandler;
import org.sevenleaves.droidsensor.handlers.StateOnHandler;
import org.sevenleaves.droidsensor.handlers.StateTurningOffHandler;
import org.sevenleaves.droidsensor.handlers.StateTurningOnHandler;

import twitter4j.TwitterException;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.Handler.Callback;
import android.util.Log;

/**
 * @author esmasui@gmail.com
 * 
 */
public class DroidSensorService extends ServiceSupport {

    /**
     * ?BluetoothOFF/ON?????.
     * Donut?Discovery????????.
     * ????????????????.
     */
    private class DonutRemoteDeviceHandler extends RemoteDeviceHandler {

        @Override
        public void onScanModeChangedConnectable() {

            getBluetoothDevice().stopPeriodicDiscovery();
            getBluetoothDevice().disable();
        }
    }

    /**
     * ?BluetoothOFF/ON?????.
     * Donut?Discovery????????.
     */
    private class DonutScanModeNoneHandler extends ScanModeNoneHandler {

        @Override
        public void onStateChangedOff() {

            getBluetoothDevice().enable();
        }
    }

    /**
     * ?Bluetooth?OFF????
     * ON?????bluetooth???????????.
     * 
     */
    private class RegisterAddressHandler extends StateTurningOnHandler {

        boolean _registerd;

        @Override
        public void onStateChangedOn() {

            if (!_registerd) {

                // TODO ??????.
                boolean succeed = registerAddress();
            }

            _registerd = true;
            super.onStateChangedOn();
        }
    }

    /**
     * Bluetooth?????????????.
     * ???ConnectableDiscoverable???????????????????.
     * 
     */
    private class RemoteDeviceHandler extends ScanModeConnectableDiscoverableHandler {

        @Override
        public void onRemoteDeviceDisappeared(String address) {

            _devices.remove(address);
        }

        @Override
        public void onRemoteDeviceFound(String address) {

            if (_devices.contains(address)) {

                return;
            }

            BluetoothDeviceStub stub = BluetoothDeviceStubFactory
                    .createBluetoothServiceStub(DroidSensorService.this);

            String name = stub.getRemoteName(address);

            if (name == null) {

                return;
            }

            DroidSensorService.this.onRemoteDeviceFound(address, name);
        }

        @Override
        public void onRemoteNameUpdated(String address, String name) {

            DroidSensorService.this.onRemoteDeviceFound(address, name);
        }
    }

    private static final int REMOTE_DEVICE_FOUND_MESSAGE = 1;

    /**
     * Log?.
     */
    private static final String TAG = DroidSensorService.class.getSimpleName();

    private static final long INTERVAL_SECONDS = 10L;

    private BluetoothStateListenerAdapter _receiver;

    private BluetoothEventControllerImpl _controller;

    private volatile boolean _started;

    private DroidSensorInquiry _droidSensorInquiry;

    /**
     * ?????????Bluetooth???.
     */
    private Set<String> _devices;

    private final RemoteCallbackList<IDroidSensorCallbackListener> _listeners = new RemoteCallbackList<IDroidSensorCallbackListener>();

    private Handler _handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            if (msg.what != REMOTE_DEVICE_FOUND_MESSAGE) {

                super.dispatchMessage(msg);

                return;
            }

            for (int i = 0, size = _listeners.beginBroadcast(); i < size; ++i) {

                IDroidSensorCallbackListener item = _listeners.getBroadcastItem(i);

                try {

                    item.deviceFound((String) msg.obj);
                } catch (RemoteException e) {

                    Log.e(TAG, e.getLocalizedMessage(), e);
                }
            }

            _listeners.finishBroadcast();
        };
    };

    private final IDroidSensorService.Stub _binder = new IDroidSensorService.Stub() {

        public void addListener(IDroidSensorCallbackListener listener) throws RemoteException {

            _listeners.register(listener);
        }

        public boolean isStarted() throws RemoteException {

            return _receiver != null;
        }

        public void removeListener(IDroidSensorCallbackListener listener) throws RemoteException {

            _listeners.unregister(listener);
        }

        public void stopService() throws RemoteException {

            DroidSensorService.this.stopService();
        }
    };

    public DroidSensorService() {

        _devices = Collections.synchronizedSet(new HashSet<String>());
    }

    @Override
    public IBinder onBind(Intent intent) {

        return _binder;
    }

    @Override
    public void onCreate() {

        Log.d(TAG, "service create");

        super.onCreate();
    }

    @Override
    public void onDestroy() {

        Log.d(TAG, "service destroy");

        super.onDestroy();

        try {

            _receiver.unregisterSelf(this);
        } catch (Exception e) {
            // nop.
        }

        if (_droidSensorInquiry != null) {

            _droidSensorInquiry.cancelAll();
            _droidSensorInquiry = null;
        }

        _receiver = null;
        _controller = null;
        _started = false;
        _devices.clear();
        BluetoothSettings.load(this);
        hideNotification();
    }

    @Override
    public void onStart(Intent intent, int startId) {

        super.onStart(intent, startId);

        if (isStopAction(intent)) {

            if (_started) {

                stopService();
            }

            return;
        }

        if (isActionContinue(intent)) {

            _started = true;

            Log.d(TAG, "running");

            if (_started) {

                if (_droidSensorInquiry == null) {

                    _droidSensorInquiry = new DroidSensorInquiry(DroidSensorService.this);
                }

                if (_receiver == null) {

                    BluetoothSettings.save(this);
                    showNotification();
                    _controller = createController();
                    _receiver = createReceiver(_controller);
                    _receiver.registerSelf(this);
                    fireEvent();
                }

                // callLater(DroidSensorService.this, IDroidSensorService.class,
                // INTERVAL_SECONDS);
                callLater(DroidSensorService.this, DroidSensorService.class, INTERVAL_SECONDS);
            }
        }
    }

    public void stopService() {

        Log.d(TAG, "stopService");
        cancelImmediatly(this, DroidSensorService.class);
        stopSelf();

        try {

            _receiver.unregisterSelf(this);
        } catch (Exception e) {
            // nop.
        }

        if (_droidSensorInquiry != null) {

            _droidSensorInquiry.cancelAll();
            _droidSensorInquiry = null;
        }

        _receiver = null;
        _controller = null;
        hideNotification();
        _started = false;
        _devices.clear();
        BluetoothSettings.load(this);
    }

    /**
     * ??????.
     * 
     * @return
     */
    private BluetoothEventControllerImpl createController() {

        BluetoothEventControllerImpl res = new BluetoothEventControllerImpl(this);
        // res.addHandler(new ScanModeConnectableDiscoverableHandler());
        RemoteDeviceHandler remoteDeviceHandler = createRemoteDeviceHandler();
        res.addHandler(remoteDeviceHandler);

        res.addHandler(new ScanModeConnectableHandler());
        // res.addHandler(new ScanModeNoneHandler());
        ScanModeNoneHandler scanModeNoneHandler = createScanModeNoneHandler();
        res.addHandler(scanModeNoneHandler);

        res.addHandler(new StateOffHandler());

        // res.addHandler(new StateTurningOnHandler());
        res.addHandler(new RegisterAddressHandler());

        res.addHandler(new StateOnHandler());
        res.addHandler(new StateTurningOffHandler());
        res.addHandler(new DiscoveryStartedHandler());
        res.addHandler(new DiscoveryCompletedHandler());

        return res;
    }

    private BluetoothStateListenerAdapter createReceiver(BluetoothEventControllerImpl controller) {

        BluetoothStateListenerAdapter res = new BluetoothStateListenerAdapter();
        res.setHandler(controller);

        return res;
    }

    /**
     * {@link RemoteDeviceHandler}?.
     * toggleBluetoothOnOff????Donut?????.
     * 
     * @return
     */
    private RemoteDeviceHandler createRemoteDeviceHandler() {

        SettingsManager settings = SettingsManager.getInstance(DroidSensorService.this);

        if (settings.isToggleBluetooth()) {

            return new DonutRemoteDeviceHandler();
        }

        return new RemoteDeviceHandler();
    }

    /**
     * 
     * {@link ScanModeNoneHandler}?.
     * toggleBluetoothOnOff????Donut?????.
     * 
     * @return
     */
    private ScanModeNoneHandler createScanModeNoneHandler() {

        SettingsManager settings = SettingsManager.getInstance(DroidSensorService.this);

        if (settings.isToggleBluetooth()) {

            return new DonutScanModeNoneHandler();
        }

        return new ScanModeNoneHandler();
    }

    /**
     * ?????.
     */
    private void fireEvent() {

        BluetoothDeviceStub bt = _controller.getBluetoothDevice();

        if (bt.isEnabled()) {

            boolean succeed = registerAddress();

            if (bt.isDiscovering()) {

                _controller.setCurrentState(BluetoothState.DISCOVERY_STARTED);
                bt.cancelDiscovery();

                return;
            }

            _controller.setCurrentState(BluetoothState.BLUETOOTH_STATE_ON);
            bt.setScanMode(0x3);

            return;
        }

        _controller.setCurrentState(BluetoothState.BLUETOOTH_STATE_OFF);
        bt.enable();
    }

    /**
     * ????????????.
     * 
     * @param settings
     * @param id
     * @return ???true????false?.
     */
    private boolean isDeviceWillTweet(SettingsManager settings, String id) {

        String myId = settings.getTwitterId();

        if (isUser(id, myId)) {

            return true;
        }

        if (!settings.isAllBluetoothDevices()) {

            return false;
        }

        if (isPassedByUser(id, myId)) {

            return settings.isDetailPassedUser();
        }

        if (isPassedByMe(id, myId)) {

            return settings.isDetailPassedMe();
        }

        return settings.isDetailPassedNo();
    }

    /**
     * ?????????????.
     * 
     * @param bluetooth
     * @return ????????true????????false?.
     */
    private boolean isDiscoverable(BluetoothDeviceStub bluetooth) {

        boolean res = (bluetooth.getScanMode() == BluetoothDeviceStub.SCAN_MODE_CONNECTABLE_DISCOVERABLE);

        return res;
    }

    /**
     * ???????.
     * 
     * @param str
     * @return ???true???????false?.
     */
    private boolean isEmpty(String str) {

        if (str == null) {

            return true;
        }

        if (str.trim().length() == 0) {

            return true;
        }

        return false;
    }

    /**
     * ?????????????????.
     * 
     * @param id
     * @param myId
     * @return
     */
    private boolean isPassedByMe(String id, String myId) {

        return "@".concat(myId).equals(id);
    }

    /**
     * ??????????????????.
     * 
     * @param id
     * @param myId
     * @return
     */
    private boolean isPassedByUser(String id, String myId) {

        String s = safeString(id);

        if (!s.startsWith("@")) {

            return false;
        }

        return !isPassedByMe(id, myId);
    }

    /**
     * ??????????????????.
     * 
     * @param id
     * @param myId
     * @return
     */
    private boolean isUser(String id, String myId) {

        if (isEmpty(id)) {

            return false;
        }

        if (isPassedByMe(id, myId)) {

            return false;
        }

        if (isPassedByUser(id, myId)) {

            return false;
        }

        return true;
    }

    /**
     * ????
     * 
     * @param address
     * @param name
     */
    private void onRemoteDeviceFound(final String address, String name) {

        if (_devices.contains(address)) {

            return;
        }

        // device-found, name-update???????
        // ????twitterID???????????????????
        if (name == null) {

            return;
        }

        BluetoothDeviceStub bluetooth = BluetoothDeviceStubFactory
                .createBluetoothServiceStub(DroidSensorService.this);

        // ????????????????????
        if (!isDiscoverable(bluetooth)) {

            return;
        }

        SettingsManager settings = SettingsManager.getInstance(DroidSensorService.this);

        final String fixedName;

        if (name.trim().length() > 0) {

            fixedName = name;
        } else {

            fixedName = "*unknown*";
        }

        _droidSensorInquiry.getTwitterUser(address, settings.getTwitterId(), settings.getDefaultMessage(),
                new Callback() {

                    public boolean handleMessage(Message msg) {

                        Bundle data = msg.getData();
                        String id = data.getString(DroidSensorInquiry.TWITTER_USER);
                        String message = data.getString(DroidSensorInquiry.MESSAGE);
                        Log.d(TAG, "id:" + id);

                        tweetDeviceFound(address, fixedName, id, message);

                        return true;
                    }
                });

    }

    private void persistBluetoothDevice(final String address, final String name, final String twitterID,
            final String message, final boolean tweeted) {

        DatabaseManipulation.manipulate(this, new ManipulationScope() {

            public void execute(SQLiteDatabase db) {

                BluetoothDeviceStub stub = BluetoothDeviceStubFactory
                        .createBluetoothServiceStub(DroidSensorService.this);

                BluetoothDeviceEntityDAO dao = new BluetoothDeviceEntityDAO(db);

                BluetoothDeviceEntity e;

                // ??????.
                // ohgro:???????????????????????????????????????????????#droidsensor
                e = new BluetoothDeviceEntity();
                e.setAddress(address);
                // e.setRSSI();
                e.setName(name);
                e.setDeviceClass(stub.getRemoteClass(address));
                e.setCompany(stub.getRemoteCompany(address));
                e.setManufacturer(stub.getRemoteManufacturer(address));
                e.setTwitterID(twitterID);
                e.setMessage(message);
                // e.setLongitude();
                // e.setLatitude();
                e.setCount(1);
                // e.setStatus();
                e.setStatus(tweeted ? 1 : 0);
                e.setUpdated(Calendar.getInstance().getTimeInMillis());
                dao.insert(e);

            }
        });
    }

    /**
     * DroidSensor??Bluetooth?????.
     * 
     * @return
     */
    private boolean registerAddress() {

        BluetoothDeviceStub stub = BluetoothDeviceStubFactory.createBluetoothServiceStub(this);
        SettingsManager settings = SettingsManager.getInstance(this);
        String address = stub.getAddress();

        try {

            // second-account????(address????)

            Log.d(TAG, "register twitter ID");

            boolean succeed = DroidSensorUtils.putTwitterId(settings.getApiUrl(), address, settings.getTwitterId(),
                    settings.getDefaultMessage());

            return succeed;
        } catch (Exception e) {

            return false;
        }
    }

    private String safeString(String str) {

        if (isEmpty(str)) {

            return "";
        }

        return str.trim();
    }

    private void sendMessage(String address) {

        Message msg = new Message();
        msg.obj = address;
        msg.what = REMOTE_DEVICE_FOUND_MESSAGE;
        _handler.sendMessage(msg);
    }

    private String convertSpecialDevice(String name, String device) {

        if (name == null) {

            return device;
        }

        String lower = name.toLowerCase();

        if (lower.contains("iphone")) {

            return "iPhone";
        }

        if (lower.contains("ipod")) {

            return "iPod";
        }

        return device;
    }

    private void tweetDeviceFound(String address, String name, String id, String message) {

        SettingsManager settings = SettingsManager.getInstance(DroidSensorService.this);

        if (!isDeviceWillTweet(settings, id)) {

            persistBluetoothDevice(address, name, id, message, false);
            sendMessage(address);
            _devices.add(address);

            return;
        }

        String tweeted;
        BluetoothDeviceStub bluetooth = BluetoothDeviceStubFactory.createBluetoothServiceStub(this);
        int btClass = bluetooth.getRemoteClass(address);

        try {

            tweeted = TwitterUtils.tweetDeviceFound(address, name, id, message,
                    convertSpecialDevice(name, BluetoothUtils.getMajorDeviceClassName(this, btClass)), settings);
        } catch (TwitterException e) {

            if (e.getStatusCode() == 401) {

                String error = "Could not authenticate you.";
                String pref = "Error from Twitter:";

                showDeviceFound(pref + error);
            }

            Log.e(TAG, e.getLocalizedMessage(), e);

            return;
        }

        if (tweeted == null) {

            return;
        }

        Log.d(TAG, address + "(" + name + ")" + " found.");

        persistBluetoothDevice(address, name, id, message, true);
        _devices.add(address);

        sendMessage(address);
        showDeviceFound(tweeted);
    }
}