org.peercast.core.PeerCastService.java Source code

Java tutorial

Introduction

Here is the source code for org.peercast.core.PeerCastService.java

Source

package org.peercast.core;

/**
 * (c) 2013, T Yoshizawa
 *
 * Dual licensed under the MIT or GPL licenses.
 */
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class PeerCastService extends Service {
    static final String TAG = "PeCaSrv";

    public static final int MSG_GET_APPLICATION_PROPERTIES = 0x00;
    public static final int MSG_GET_CHANNELS = 0x01;
    public static final int MSG_GET_STATS = 0x02;

    public static final int MSG_CMD_CHANNEL_BUMP = 0x10;
    public static final int MSG_CMD_CHANNEL_DISCONNECT = 0x11;
    public static final int MSG_CMD_CHANNEL_KEEP_YES = 0x12;
    public static final int MSG_CMD_CHANNEL_KEEP_NO = 0x13;

    private static final int NOTIFY_ID_FOREGROUND = Integer.MAX_VALUE; //0
    private NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);

    private Messenger serviceMessenger;

    private ServiceHandler serviceHandler;
    private Looper serviceLooper;

    private Timer tRefreshNotify;
    private static final int intervaltRefreshNotify = 3000;
    private boolean isShowNotification;

    private Map<String, ChannelInfo> activeChInfos = new LinkedHashMap<String, ChannelInfo>();
    private boolean isRunning;

    private static class ServiceHandler extends Handler {
        public ServiceHandler(Looper serviceLooper) {
            super(serviceLooper);
        }

        @Override
        public void handleMessage(Message msg) {
            Message reply = obtainMessage(msg.what);
            Bundle data;
            switch (msg.what) {
            case MSG_GET_APPLICATION_PROPERTIES:
                data = nativeGetApplicationProperties();
                break;

            case MSG_GET_CHANNELS:
                data = nativeGetChannels();
                break;

            case MSG_GET_STATS:
                data = nativeGetStats();
                break;

            case MSG_CMD_CHANNEL_BUMP:
            case MSG_CMD_CHANNEL_DISCONNECT:
            case MSG_CMD_CHANNEL_KEEP_YES:
            case MSG_CMD_CHANNEL_KEEP_NO:
                nativeChannelCommand(msg.what, msg.arg1);
                return;

            default:
                Log.e(TAG, "Invalid msg.what=" + msg.what);
                return;
            }
            reply.setData(data);
            try {
                if (msg.replyTo == null) {
                    Log.e(TAG, "msg.replyTo==null");
                    return;
                }
                msg.replyTo.send(reply);
            } catch (RemoteException e) {
                Log.e(TAG, "msg.replyTo.send(reply)", e);
            }
        }
    }

    private Notification createNotification() {
        if (activeChInfos.isEmpty()) {
            //?????????
            isShowNotification = false;
            return new Notification();
        }

        Stats stats = Stats.fromNativeResult(nativeGetStats());

        String title = "PeerCast";
        String text = "..";
        Intent intent = new Intent();
        //???????????
        for (ChannelInfo info : activeChInfos.values()) {
            title = "Playing: " + info.getName();
            text = info.getDesc();
            text += "  ";
            text += info.getComment();
            if (info.getUrl() != null) {
                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(info.getUrl()));
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            break;
        }

        String info = String.format("D %.1f / U %.1f kbs ", stats.getInBytes() / 1024f * 8,
                stats.getOutBytes() / 1024f * 8);
        info += String.format("[%d / %d MB]", stats.getInTotalBytes() / 1024 / 1024,
                stats.getOutTotalBytes() / 1024 / 1024);

        Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_notify_icon);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);

        return notificationBuilder.setContentTitle(title).setContentText(text).setContentInfo(info)
                .setContentIntent(contentIntent).setSmallIcon(R.drawable.ic_notify_icon).setLargeIcon(icon).build();
    }

    @Override
    public void onCreate() {
        super.onCreate();

        HandlerThread thread = new HandlerThread("PeerCastService", Thread.MIN_PRIORITY);
        thread.start();

        serviceLooper = thread.getLooper();
        serviceHandler = new ServiceHandler(serviceLooper);
        serviceMessenger = new Messenger(serviceHandler);
    }

    //html??? /data/data/org.peercast.core/ ?
    private void assetInstall() throws IOException {
        File dataDir = getFilesDir();

        ZipInputStream zipIs = new ZipInputStream(getAssets().open("peca.zip"));
        try {
            ZipEntry ze;
            while ((ze = zipIs.getNextEntry()) != null) {
                File file = new File(dataDir, ze.getName());
                if (file.exists())
                    continue;

                if (ze.isDirectory()) {
                    file.mkdirs();
                    continue;
                }
                file.getParentFile().mkdirs();
                Log.d(TAG, "Install resource -> " + file.getAbsolutePath());
                FileOutputStream fout = new FileOutputStream(file);
                byte[] buffer = new byte[1024];
                int length = 0;
                while ((length = zipIs.read(buffer)) > 0) {
                    fout.write(buffer, 0, length);
                }
                zipIs.closeEntry();
                fout.close();
            }
        } finally {
            zipIs.close();
        }
    }

    @Override
    public IBinder onBind(Intent i) {
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

        String iniPath = getFileStreamPath("peercast.ini").getAbsolutePath();
        String resDirPath = getFilesDir().getAbsolutePath();

        boolean showNotify = pref.getBoolean(PeerCastMainActivity.pref_notification, true);

        synchronized (this) {
            if (!isRunning) {
                try {
                    assetInstall();
                } catch (IOException e) {
                    Log.e(TAG, "html-dir install failed.");
                    return null;
                }

                nativeStart(iniPath, resDirPath);
                startForeground(NOTIFY_ID_FOREGROUND, new Notification());
                if (showNotify) {
                    tRefreshNotify = new Timer(false);
                    tRefreshNotify.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            if (!isShowNotification) {
                                return;
                            }
                            NotificationManager nm = (NotificationManager) getSystemService(
                                    Context.NOTIFICATION_SERVICE);
                            Notification n = createNotification();
                            nm.notify(NOTIFY_ID_FOREGROUND, n);
                        }
                    }, intervaltRefreshNotify, intervaltRefreshNotify);
                }
                isRunning = true;
            }
        }
        return serviceMessenger.getBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return false;
    }

    @Override
    public void onDestroy() {
        stopForeground(true);
        serviceLooper.quit();
        if (tRefreshNotify != null) {
            tRefreshNotify.cancel();
        }
        nativeQuit();
    }

    /**<pre>
     * native called:
     * 
     * AndroidPeercastApp::notifyMessage(
     *       ServMgr::NOTIFY_TYPE tNotify, 
     *       const char *message)
     * 
     * */
    private void notifyMessage(int notifyType, String message) {
        if (BuildConfig.DEBUG) {
            Log.i(TAG, "notifyMessage: " + notifyType + ", " + message);
        }
    }

    private static final int NOTIFY_CHANNEL_START = 0;
    private static final int NOTIFY_CHANNEL_UPDATE = 1;
    private static final int NOTIFY_CHANNEL_STOP = 2;

    /**
     * <pre>
     * native called:
     * 
     * AndroidPeercastApp:: 
     *   channelStart(ChanInfo *info) 
     *   channelUpdate(ChanInfo *info)
     *   channelStop(ChanInfo *info)
     * **/
    private void notifyChannel(int notifyType, Bundle bInfo) {
        ChannelInfo info = new ChannelInfo(bInfo);
        String chId = info.getId();

        switch (notifyType) {
        case NOTIFY_CHANNEL_START:
            //Notification??
            activeChInfos.put(chId, info);
            isShowNotification = true;
            break;
        case NOTIFY_CHANNEL_UPDATE:
            if (activeChInfos.containsKey(chId))
                activeChInfos.put(chId, info);
            break;
        case NOTIFY_CHANNEL_STOP:
            activeChInfos.remove(chId);
            break;
        }

        if (BuildConfig.DEBUG) {
            String s = bInfo.getString("id");
            for (String key : bInfo.keySet())
                s += ", " + key + "=" + bInfo.get(key);
            Log.i(TAG, "notifyType_" + notifyType + ": " + s);
        }
    }

    /**
     * PeerCast???
     * 
     * @param iniPath
     *            peercast.ini?(????)
     * @param resourceDir
     *            html??
     * @return ?
     */
    private native int nativeStart(String iniPath, String resourceDir);

    private native void nativeQuit();

    /**
     * <pre>
     *  ??Channel????
     * Channel[] channels = Channel.fromNativeResult(nativeGetChannels())
     *   
     * 
     * @return Bundle
     */
    private static native Bundle nativeGetChannels();

    /**
     * <pre>
     * Stats stats = Stats.fromNativeResult(nativeGetStats())
     * 
     * 
     * @return Bundle
     */
    private static native Bundle nativeGetStats();

    private static native Bundle nativeGetApplicationProperties();

    private static native void nativeChannelCommand(int cmdType, int channel_id);

    private static native void nativeClassInit();

    static {
        System.loadLibrary("peercast");
        nativeClassInit();
    }

}