Android Open Source - cicada Cicada Service






From Project

Back to project page cicada.

License

The source code is released under:

Apache License

If you think the Android project cicada listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

// Copyright (C) 2011 Cicada contributors
////  w ww .j a v  a  2s. c o  m
// 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.cicadasong.cicada;

package org.cicadasong.cicada;

import java.util.HashMap;
import java.util.Map;

import org.cicadasong.apollo.ApolloConfig;
import org.cicadasong.apollo.ApolloIntents;
import org.cicadasong.apollo.ApolloIntents.ButtonPress;
import org.cicadasong.apollo.BitmapUtil;
import org.cicadasong.cicada.MetaWatchConnection.Mode;
import org.cicadasong.cicadalib.CicadaApp;
import org.cicadasong.cicadalib.CicadaApp.AppType;
import org.cicadasong.cicadalib.CicadaIntents;
import org.cicadasong.cicadalib.CicadaNotification;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class CicadaService extends Service {
  public static final String TAG = Cicada.TAG;
  
  public static final String INTENT_LAUNCH_APP = "org.cicadasong.cicada.LAUNCH_APP";
  public static final String INTENT_SERVICE_STARTED = "org.cicadasong.cicada.SERVICE_STARTED";
  public static final String INTENT_SERVICE_STOPPED = "org.cicadasong.cicada.SERVICE_STOPPED";
  public static final String EXTRA_APP_PACKAGE = "app_package";
  public static final String EXTRA_APP_CLASS = "app_class";
  public static final String EXTRA_APP_SETTINGS_CLASS = "app_settings_class";
  public static final String EXTRA_APP_NAME = "app_name";
  
  public static final boolean USE_DEVICE_SERVICE = true;
  
  // Special AppDescription, since idle screen isn't a real app.
  public static final AppDescription IDLE_SCREEN =
      new AppDescription("IDLE_SCREEN", "IDLE_SCREEN", null, "Idle Screen", AppType.APP);
  
  private BroadcastReceiver receiver;
  private static AppDescription activeApp;
  private AppConnection activeConnection;
  private int sessionId = 1;
  private WidgetScreen widgetScreen = new WidgetScreen();
  private boolean launchedFromWidgetScreen = false;
  
  private static boolean isRunning = false;
  private static byte[] screenBuffer;
  
  private Map<Byte, AppDescription> hotkeys = new HashMap<Byte, AppDescription>();
  private DeviceServiceConnection deviceServiceConnection;
  private MetaWatchConnection.Listener connectionListener;
  private final Messenger appMessenger = new Messenger(new AppHandler());
  
  private NotificationRenderer notificationRenderer;

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

  @Override
  public void onCreate() {
    IntentFilter filter = new IntentFilter();
    filter.addAction(CicadaIntents.INTENT_PUSH_CANVAS);
    filter.addAction(CicadaIntents.INTENT_VIBRATE);
    filter.addAction(CicadaIntents.INTENT_START_NOTIFICATION);
    filter.addAction(CicadaIntents.INTENT_STOP_NOTIFICATION);
    filter.addAction(ApolloIntents.INTENT_BUTTON_PRESS);
    filter.addAction(HotkeySetupActivity.INTENT_HOTKEYS_CHANGED);
    filter.addAction(WidgetSetup.INTENT_WIDGETS_CHANGED);
    filter.addAction(INTENT_LAUNCH_APP);
    
    receiver = createBroadcastReceiver();
    registerReceiver(receiver, filter);

    loadHotkeys();

    notificationRenderer = new NotificationRenderer(this);
    
    Log.v(Cicada.TAG, "Cicada Service Started");
    isRunning = true;
    sendBroadcast(new Intent(INTENT_SERVICE_STARTED));

    super.onCreate();
  }
  
  private MetaWatchConnection.Listener createConnectionListener() {
    return new MetaWatchConnection.Listener() {
      @Override
      public void buttonPressed(ButtonPress event) {
        handleButtonPress(event);
      }

      @Override
      public void modeChanged(Mode mode) {
        Log.v(TAG, "Mode changed: " + mode);
      }

      @Override
      public void displayTimedOut(Mode mode) {
        Log.v(TAG, "Display timed out for mode: " + mode);
        deviceServiceConnection.getService().setMode(Mode.APPLICATION);
      }
    };
  }
  
  public static boolean isRunning() {
    return isRunning;
  }
  
  public static byte[] getLastScreenBuffer() {
    return screenBuffer;
  }
  
  public static AppDescription getActiveApp() {
    return activeApp;
  }
  
  private void loadHotkeys() {
    AppDatabase db = new AppDatabase(this);
    hotkeys.clear();
    for (HotkeySetupEntry entry : db.getHotkeySetup()) {
      hotkeys.put(entry.hotkeys, entry.app);
    }
    db.close();
  }
  
  /**
   * Handler of incoming messages from apps.
   */
  class AppHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
        case CicadaApp.MESSAGETYPE_PUSH_CANVAS:
          Log.v(TAG, "Received push canvas message");
          handleScreenUpdate(msg.getData());
          break;
        default:
          super.handleMessage(msg);
      }
    }
  }

  private BroadcastReceiver createBroadcastReceiver() {
    return new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        Log.v(Cicada.TAG, "Received intent: " + intent);
        if (intent.getAction().equals(CicadaIntents.INTENT_PUSH_CANVAS)) {
          handleScreenUpdate(intent.getExtras());
        } else if (intent.getAction().equals(CicadaIntents.INTENT_VIBRATE)) {
          handleVibrationRequest(intent.getExtras());
        } else if (intent.getAction().equals(ApolloIntents.INTENT_BUTTON_PRESS)) {
          ButtonPress buttonPress = ApolloIntents.ButtonPress.parseIntent(intent);
          handleButtonPress(buttonPress);
        } else if (intent.getAction().equals(INTENT_LAUNCH_APP)) {
          String packageName = intent.getExtras().getString(EXTRA_APP_PACKAGE);
          String className = intent.getExtras().getString(EXTRA_APP_CLASS);
          String settingsClassName = intent.getExtras().getString(EXTRA_APP_SETTINGS_CLASS);
          String appName = intent.getExtras().getString(EXTRA_APP_NAME);
          AppDescription app =
              new AppDescription(packageName, className, settingsClassName, appName, AppType.APP);
          switchToApp(app);
        } else if (intent.getAction().equals(HotkeySetupActivity.INTENT_HOTKEYS_CHANGED)) {
          loadHotkeys();
        } else if (intent.getAction().equals(WidgetSetup.INTENT_WIDGETS_CHANGED)) {
          if (WidgetScreen.DESCRIPTION.equals(activeApp)) {
            deactivateWidgets();
            activateWidgets();
          }
        } else if (intent.getAction().equals(CicadaIntents.INTENT_START_NOTIFICATION)) {
          handleStartNotification(intent.getExtras());
        } else if (intent.getAction().equals(CicadaIntents.INTENT_STOP_NOTIFICATION)) {
          handleStopNotification(intent.getExtras());
        }
      }
    };
  }

  private void handleScreenUpdate(Bundle parameters) {
    byte[] buffer = parameters.getByteArray(CicadaIntents.EXTRA_BUFFER);
    int senderSessionId = parameters.getInt(CicadaIntents.EXTRA_SESSION_ID, 0);
    
    if (widgetScreen.hasSessionId(senderSessionId)) {
      buffer = widgetScreen.updateScreenBuffer(buffer, senderSessionId);
    } else if (senderSessionId != sessionId) {
      Log.e(Cicada.TAG, "App tried to push screen using expired sessionId " + senderSessionId
          + " -- the current sessionId is " + sessionId);
      return;
    }
    
    screenBuffer = buffer;
    if (USE_DEVICE_SERVICE && (deviceServiceConnection != null)) {
      deviceServiceConnection.getService().updateScreen(
              buffer, Mode.APPLICATION);
    }

    // Do this even if we're using the device service in order to update simulated display
    // in the app.
    Intent newIntent = new Intent(ApolloIntents.INTENT_PUSH_BITMAP);
    newIntent.putExtra(ApolloIntents.EXTRA_BITMAP_BUFFER, buffer);
    sendBroadcast(newIntent);
  }

  private void handleVibrationRequest(Bundle parameters) {
    int senderSessionId = parameters.getInt(CicadaIntents.EXTRA_SESSION_ID, 0);
    if (senderSessionId != sessionId) {
      Log.e(Cicada.TAG, "App tried to vibrate using expired sessionId " + senderSessionId
          + " -- the current sessionId is " + sessionId);
      return;
    }
    
    int onMillis = parameters.getInt(CicadaIntents.EXTRA_VIBRATE_ON_MSEC, 0);
    int offMillis = parameters.getInt(CicadaIntents.EXTRA_VIBRATE_OFF_MSEC, 0);
    int numCycles = parameters.getInt(CicadaIntents.EXTRA_VIBRATE_NUM_CYCLES, 0);
    
    if (USE_DEVICE_SERVICE && (deviceServiceConnection != null)) {
      deviceServiceConnection.getService().vibrate(onMillis, offMillis, numCycles);
    } else {
      Intent newIntent = new Intent(ApolloIntents.INTENT_VIBRATE);
      newIntent.putExtra(ApolloIntents.EXTRA_VIBRATE_ON_MSEC, onMillis);
      newIntent.putExtra(ApolloIntents.EXTRA_VIBRATE_OFF_MSEC, offMillis);
      newIntent.putExtra(ApolloIntents.EXTRA_VIBRATE_NUM_CYCLES, numCycles);
      sendBroadcast(newIntent);
    }
  }

  protected void handleButtonPress(ButtonPress buttonPress) {
    byte buttons = buttonPress.getButton();
    Log.v(Cicada.TAG, "Got button press: " + buttons);
    if (buttonPress != null) {
      if (buttonPress.hasButtonsPressed(ApolloConfig.Button.TOP_LEFT)) {
        if (launchedFromWidgetScreen) {
          switchToApp(WidgetScreen.DESCRIPTION);
        } else if (!AppList.DESCRIPTION.equals(activeApp)) {
          switchToApp(AppList.DESCRIPTION);
        } else {
          activeConnection.sendButtonEvent(buttons);
        }
      } else if (IDLE_SCREEN.equals(activeApp)) {
        if (hotkeys.containsKey(buttons)) {
          switchToApp(hotkeys.get(buttons));
        }
      } else if (WidgetScreen.DESCRIPTION.equals(activeApp)) {
        Log.v(TAG, "WIDGET TEST");
        int appIndex = -1;
        if (buttonPress.hasButtonsPressed(ApolloConfig.Button.TOP_RIGHT)){
          appIndex = 0;
        } else if (buttonPress.hasButtonsPressed(ApolloConfig.Button.MIDDLE_RIGHT)){
          appIndex = 1;
        } else if (buttonPress.hasButtonsPressed(ApolloConfig.Button.BOTTOM_RIGHT)){
          appIndex = 2;
        }
        if (appIndex >= 0) {
          AppDescription app = widgetScreen.widgets[appIndex];
          if (app != null && app.supportsFullScreenMode()) {
            switchToApp(app);
          }
        }
      } else if (activeApp != null) {
        activeConnection.sendButtonEvent(buttons);
      }
    }
  }
  
  private void handleStartNotification(Bundle params) {
    CicadaNotification note = CicadaNotification.createFromBundle(params);
    Log.v(TAG, "Got start notification from " +
        note.getPackageName() + "/" + note.getClassName() + ": " + note.getText());

    byte[] notificationBuffer =
        BitmapUtil.bitmapToBuffer(notificationRenderer.renderNotification(note));
    if (USE_DEVICE_SERVICE && (deviceServiceConnection != null)) {
      deviceServiceConnection.getService().updateScreen(
          notificationBuffer, Mode.NOTIFICATION);
      
      // TODO: Get this info from the notification
      deviceServiceConnection.getService().vibrate(500, 0, 1);
    }
  }
  
  private void handleStopNotification(Bundle params) {
    CicadaNotification note = CicadaNotification.createFromBundle(params);
    Log.v(TAG, "Got start notification from " +
        note.getPackageName() + "/" + note.getClassName() + ": " + note.getText());
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    startForeground();
    
    if (USE_DEVICE_SERVICE) {
      deviceServiceConnection = new DeviceServiceConnection();
      deviceServiceConnection.connect();
    }
    
    if (activeApp != null) {
      deactivateApp(activeApp);
    }
    
    if (!PrefUtil.getAppScanCompleted(this)) {
      Log.v(Cicada.TAG, "Scanning for Cicada apps...");
      AppScanner scanner = new AppScanner(this, new AppScanner.Listener() {
        @Override
        public void scanFinished() {
          Log.v(Cicada.TAG, "App scan complete");
          PrefUtil.setAppScanCompleted(CicadaService.this, true);
          switchToApp(AppList.DESCRIPTION);
        }
      });
      scanner.execute((Void) null);
    } else {
      switchToApp(AppList.DESCRIPTION);
    }
    
    ApolloIntents.setApplicationMode(this, true);
    ApolloIntents.pushText(this, getResources().getString(R.string.welcome_on_screen));
    ApolloIntents.vibrate(this, 300, 0, 1);
    
    return super.onStartCommand(intent, flags, startId);
  }

  private void startForeground() {
    String notificationTitle = getString(R.string.notification_title);
    String notificationBody = getString(R.string.notification_body);

    Notification notification = new Notification(R.drawable.statusbar, notificationTitle, 0);

    // The PendingIntent to launch our activity if the user selects this notification
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
            new Intent(this, Cicada.class), 0);

    // Set the info for the views that show in the notification panel.
    notification.setLatestEventInfo(
        getApplicationContext(), notificationTitle, notificationBody, contentIntent);
    notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
    startForeground(1, notification);
  }
  
  @Override
  public void onDestroy() {
    Log.v(Cicada.TAG, "Cicada Service Destroyed");
    
    if (deviceServiceConnection != null) {
      deviceServiceConnection.disconnect();
      deviceServiceConnection = null;
    }
    
    if (activeApp != null) {
      deactivateApp(activeApp);
    }
    
    unregisterReceiver(receiver);
    ApolloIntents.setApplicationMode(this, false);
    
    stopForeground(true);
    
    isRunning = false;
    sendBroadcast(new Intent(INTENT_SERVICE_STOPPED));
    
    super.onDestroy();
  }
  
  private void incrementSessionId() {
    sessionId = (sessionId + 1) % Short.MAX_VALUE;
    if (sessionId == 0) {
      sessionId++;  // Make sure 0 is never a valid session ID, since that's the uninitialized value
    }
  }
  
  private void switchToApp(AppDescription app) {
    if (app.equals(activeApp)) {
      return;
    }
    
    launchedFromWidgetScreen =
        WidgetScreen.DESCRIPTION.equals(activeApp) && !AppList.DESCRIPTION.equals(app);

    if (activeApp != null) {
      deactivateApp(activeApp);
    }
    incrementSessionId();
    activateApp(app, AppType.APP);
  }
  
  private void activateApp(AppDescription app, AppType mode) {
    Log.v(Cicada.TAG,
        "Activating app: " + app.appName + " (" + app.packageName + "/" + app.className + ")");
    activeApp = app;
    
    if (IDLE_SCREEN.equals(app)) {
      Log.v(Cicada.TAG, "Switching to idle screen...");
      ApolloIntents.pushText(this, getResources().getString(R.string.waiting_for_idle_screen));
      ApolloIntents.setApplicationMode(this, false);
      return;
    } else if (WidgetScreen.DESCRIPTION.equals(app)) {
      activateWidgets();
      return;
    }
    
    activeConnection = new AppConnection(app, sessionId, AppType.APP);
    activeConnection.connect();
  }
  
  private void deactivateApp(AppDescription app) {
    Log.v(Cicada.TAG, "Dectivating app: " + app.appName);
    activeApp = null;

    if (IDLE_SCREEN.equals(app)) {
      ApolloIntents.setApplicationMode(this, true);
      return;
    } else if (WidgetScreen.DESCRIPTION.equals(app)) {
      deactivateWidgets();
      return;
    }
    
    activeConnection.deactivateApp();
    activeConnection.disconnect();
    activeConnection = null;
  }

  private void activateWidgets() {
    AppDatabase db = new AppDatabase(this);
    widgetScreen.widgets = db.getWidgetSetup().toArray(new AppDescription[]{});
    db.close();
    
    widgetScreen.clearBuffer();
    boolean hasWidgets = false;
    
    for (int i = 0; i < widgetScreen.widgets.length; i++) {
      AppDescription app = widgetScreen.widgets[i];
      if (app != null) {
        hasWidgets = true;
        widgetScreen.putSessionId(i, sessionId);
        AppConnection connection = new AppConnection(app, sessionId, AppType.WIDGET);
        widgetScreen.widgetConnections[i] = connection;
        connection.connect();
        incrementSessionId();
      }
    }
    
    if (!hasWidgets) {
      ApolloIntents.pushText(this, getResources().getString(R.string.no_widgets_set_up));
    }
  }
  
  private void deactivateWidgets() {
    for (int i = 0; i < widgetScreen.widgets.length; i++) {
      AppConnection connection = widgetScreen.widgetConnections[i];
      if (connection != null) {
        connection.deactivateApp();
        connection.disconnect();
        widgetScreen.widgetConnections[i] = null;
      }
    }
    
    widgetScreen.clearSessionIds();
  }
  
  private void appDisconnectedUnexpectedly(AppConnection connection) {
    Log.v(Cicada.TAG, "App disconnected unexpectedly: " + connection.getApp());
    
    // The active app died, bump back to app list.
    if (connection.getApp().equals(activeApp)) {
      activeApp = null;
      activeConnection = null;
      switchToApp(AppList.DESCRIPTION);
    }
  }
  
  public class DeviceServiceConnection implements ServiceConnection {
    private boolean connected;
    private boolean requestedDisconnect;
    private DeviceService service;

    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {
      Log.v(Cicada.TAG, "DeviceService bound");
      service = ((DeviceService.DeviceServiceBinder) binder).getService();
      connectionListener = createConnectionListener();
      service.setListener(connectionListener);
      connected = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      Log.v(Cicada.TAG, "DeviceService disconnected");
      connected = false;
    }
    
    public void connect() {
      Log.v(Cicada.TAG, "Attempting to bind device service");
      requestedDisconnect = false;
      Intent bindingIntent = new Intent(getApplicationContext(), DeviceService.class);
      bindService(bindingIntent, this, Context.BIND_AUTO_CREATE);
    }
    
    public void disconnect() {
      requestedDisconnect = true;
      unbindService(this);
    }
    
    public DeviceService getService() {
      return service;
    }
  }

  public class AppConnection implements ServiceConnection {
    private boolean connected;
    private boolean requestedDisconnect;
    private Messenger messenger;
    private final AppDescription app;
    private int sessionId;
    private AppType mode;
    
    public AppConnection(AppDescription app, int sessionId, AppType mode) {
      this.app = app;
      this.sessionId = sessionId;
      this.mode = mode;
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      Log.v(Cicada.TAG, "Successfully connected to service for app: " + app);
      this.messenger = new Messenger(service);
      connected = true;
      activateApp();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      connected = false;
      if (!requestedDisconnect) {
        appDisconnectedUnexpectedly(this);
      }
    }
    
    public void connect() {
      Log.v(Cicada.TAG, "Attempting to bind service for app: " + app);
      requestedDisconnect = false;
      Intent bindingIntent = new Intent();
      bindingIntent.setComponent(app.getComponentName());
      bindService(bindingIntent, this, Context.BIND_AUTO_CREATE);
    }
    
    public void disconnect() {
      requestedDisconnect = true;
      unbindService(this);
    }
    
    public AppDescription getApp() {
      return app;
    }
    
    public void activateApp() {
      Message activateMessage =
          Message.obtain(null, CicadaApp.MESSAGETYPE_ACTIVATE, sessionId, mode.ordinal());
      activateMessage.replyTo = appMessenger;
      sendMessage(activateMessage);
    }
    
    public void deactivateApp() {
      sessionId = 0;
      Message deactivateMessage = Message.obtain(null, CicadaApp.MESSAGETYPE_DEACTIVATE);
      sendMessage(deactivateMessage);
    }
    
    public void sendButtonEvent(byte buttons) {
      // On the other end, CicadaApp-derived apps will be expecting a WatchButton enum value.
      Message buttonMessage =
        Message.obtain(null, CicadaApp.MESSAGETYPE_BUTTON_EVENT, buttons, 0);
      sendMessage(buttonMessage);
    }
    
    private void sendMessage(Message message) {
      if (!connected) {
        Log.w(Cicada.TAG,
            "Attempted to send message " + message + " to app " + app + " when disconnected");
        return;
      }

      try {
        messenger.send(message);
      } catch (RemoteException e) {
        Log.w(Cicada.TAG,
            "RemoteException when sending message " + message + " to app " + app + ":" + e);
      }
    }
  }
}




Java Source Code List

com.hydraproductions.cicada.StopWatch.java
org.cicadasong.apollo.ApolloConfig.java
org.cicadasong.apollo.ApolloIntents.java
org.cicadasong.apollo.BitmapUtil.java
org.cicadasong.apollo.SimulatedDisplayView.java
org.cicadasong.cicada.AppDatabase.java
org.cicadasong.cicada.AppDescription.java
org.cicadasong.cicada.AppList.java
org.cicadasong.cicada.AppScanner.java
org.cicadasong.cicada.CicadaService.java
org.cicadasong.cicada.CicadaSettingsActivity.java
org.cicadasong.cicada.Cicada.java
org.cicadasong.cicada.DeviceService.java
org.cicadasong.cicada.HotkeySetupActivity.java
org.cicadasong.cicada.HotkeySetupEntry.java
org.cicadasong.cicada.MetaWatchConnection.java
org.cicadasong.cicada.NotificationRenderer.java
org.cicadasong.cicada.PackageMonitor.java
org.cicadasong.cicada.PackageUtil.java
org.cicadasong.cicada.PrefUtil.java
org.cicadasong.cicada.WidgetScreen.java
org.cicadasong.cicada.WidgetSetup.java
org.cicadasong.cicadalib.CicadaApp.java
org.cicadasong.cicadalib.CicadaIntents.java
org.cicadasong.cicadalib.CicadaNotificationManager.java
org.cicadasong.cicadalib.CicadaNotification.java
org.cicadasong.cicadalib.TextBlock.java
org.cicadasong.samples.bitly.Bitly.java
org.cicadasong.samples.digitalclock.DigitalClock.java
org.cicadasong.samples.hellocicada.HelloCicada.java
org.cicadasong.samples.nextbuses.NextBuses.java
org.cicadasong.samples.notificationdemo.NotificationDemo.java
org.cicadasong.samples.notifications.NotificationAccessibilityService.java
org.cicadasong.samples.notifications.Notifications.java
org.cicadasong.samples.quicktext.QuickTextSetup.java
org.cicadasong.samples.quicktext.QuickText.java
org.cicadasong.samples.smsnotifier.SMSBroadcastReceiver.java
org.cicadasong.samples.tubestatus.Preferences.java
org.cicadasong.samples.tubestatus.TubeStatusSettingsActivity.java
org.cicadasong.samples.tubestatus.TubeStatus.java
org.cicadasong.samples.webimageplayer.Preferences.java
org.cicadasong.samples.webimageplayer.WebImagePlayerSettingsActivity.java
org.cicadasong.samples.webimageplayer.WebImagePlayer.java