Question

Is there any way to directly communicate with a WallpaperService from an Activity? It doesn't look like I can use the normal service communication classes because the onBind method is declared final in the WallpaperService class I'm extending. Worth noting that I'm referring to my WallpaperService not any.

Any workarounds if this isn't possible?

Was it helpful?

Solution

My solution was to use local sockets. I created an instance of a LocalServerSocket in the constructor of my wallpaper's Engine. Here's a quick implementation. Server runs on a separate thread and is directly tied to the lifecycle of MyEngine. The thread will stop when continueSocket is set to false. This happens onDestroy. Problem is that LocalServerSocket.accept() blocks until there's something to do. The workaround is to send a message to our own server so it will run through the loop again and check continueSocket (which is now false), closing the server. Check the closeSocketServer method. I have it running in onDestroy in the example but you might want to use it elsewhere like onSurfaceDestroyed and add your own sanity checks.

public class MyWallpaperService extends WallpaperService {
    @Override
    public Engine onCreateEngine() {
        return new MyEngine();
    }

    private class MyEngine extends Engine {

        private boolean continueSocket = true;

        MyEngine() {
            new Thread() {
                @Override
                public void run() {
                    try {
                        LocalServerSocket server = new LocalServerSocket("MyAddress");
                        Log.d("SERVER READY", "Server is ready.");
                        while(continueSocket) {
                            LocalSocket receiver = server.accept();
                            if(receiver != null) {
                                InputStream input = receiver.getInputStream();
                                byte[] data = IOUtils.toByteArray(input);
                                Log.d("GOT DATA", new String(data));
                            }
                        }
                        server.close();
                    } catch (IOException ex) {
                        Log.wtf("IOEXCEPTION", ex);
                    }
                }
            }.start();
        }

        @Override
        public void onDestroy() {
            closeSocketServer();
            super.onDestroy();
        }

        private void closeSocketServer() {
            continueSocket = false;
            try {
                LocalSocket socket = new LocalSocket();
                socket.connect(new LocalSocketAddress("MyAddress"));
                socket.getOutputStream().write(new byte[0]);
                socket.getOutputStream().close();
                socket.close();
            } catch (IOException ex) {
                //
            }
        }
    }
}

And in my Activity it can be as simple as this...

try {
    LocalSocket sender = new LocalSocket();
    sender.connect(new LocalSocketAddress("MyAddress"));
    String data = "Hello world!";
    Log.d("SENT DATA", data);
    sender.getOutputStream().write(data.getBytes());
    sender.getOutputStream().close();
    sender.close();
} catch (IOException ex) {
    Log.wtf("IOEXCEPTION", ex);
}

Logcat ends up looking like this:

D/SERVER READY﹕ Server is ready. (when the wallpaper starts up)
D/SENT DATA﹕ Hello world! (when the activity sends data)
D/GOT DATA﹕ Hello world! (when the wallpaper gets the data)

OTHER TIPS

In your WallpaperService onCreateEngine:

IntentFilter intentFilter = new IntentFilter("your.package.your.action");
MyBroadcastReceiver mReceiver = new MyBroadcastReceiver(mRenderer);
LocalBroadcastManager.getInstance(getApplicationContext())
        .registerReceiver(mReceiver, intentFilter);

In mRenderer's class:

public void receiveCommand(int i) {
    Log.d("got", String.valueOf(i));
}

Receiver class:

public class MyBroadcastReceiver extends BroadcastReceiver {
    private final MyRenderer _mRenderer;

    public MyBroadcastReceiver(MyRenderer mRenderer) {
        _mRenderer = mRenderer;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        _mRenderer.receiveCommand(intent.getExtra("msg", -1));
    }
}

Now call from activity:

Intent in = new Intent();
in.setAction("your.package.your.action");
in.setExtra("msg", 42);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(in);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top