blob: 8c331c96d4ea0b6ce862a5136d4175dd407691e5 [file] [log] [blame]
package com.android.clockwork.common;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashSet;
import java.util.Set;
/**
* Class to track the off body state of the device,
*
* This class relies on broadcasts from Home of the ACTION_DEVICE_ON_BODY_RECOGNITION action. If Home
* is not making these broadcasts, this tracker assumes the device is on body.
*
* NB: The current implementation might not be reliable since it relies on Home to cooperate so
* do not use this tracker if knowing the actual state of the device is critical, e.g. for keyguard.
*/
public class OffBodyTracker {
private static final String TAG = "WearConnectivity";
public interface Listener {
void onOffBodyChanged(boolean isOffBody);
}
// Duplicated from Settings Constants
static final String ACTION_DEVICE_ON_BODY_RECOGNITION =
"com.google.android.wearable.action.DEVICE_ON_BODY_RECOGNITION";
static final String EXTRA_DEVICE_ON_BODY_RECOGNITION = "is_don";
static final String EXTRA_LAST_CHANGED_TIME = "last_changed_time";
private final Set<Listener> mListeners;
private boolean mIsOffBody = false;
private boolean mIsScreenOff = false;
@VisibleForTesting
final BroadcastReceiver mOffBodyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, String.format("OffBodyTracker: onReceive(%s)", intent));
}
final boolean wasOffBody = isOffBody();
final String action = intent.getAction();
if (ACTION_DEVICE_ON_BODY_RECOGNITION.equals(action)) {
// Most of our code talks about being "off body", but this particular broadcast
// carries the state of "on body" so we negate it.
final boolean isRecognizedOffBody =
!intent.getBooleanExtra(EXTRA_DEVICE_ON_BODY_RECOGNITION, true);
final long lastChangedTime =
intent.getLongExtra(EXTRA_LAST_CHANGED_TIME, 0L);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, String.format("On Body Event: %s, last change: %d",
isRecognizedOffBody ? "off body" : "on body", lastChangedTime));
}
mIsOffBody = isRecognizedOffBody;
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mIsScreenOff = true;
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mIsScreenOff = false;
}
if (isOffBody() == wasOffBody) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "OffBodyTracker: no effective change in off body state");
}
return;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG,
String.format("OffBodyTracker: informing %d listeners of change to %s",
mListeners.size(),
isOffBody() ? "off body" : "on body"));
}
for (Listener listener : mListeners) {
listener.onOffBodyChanged(isOffBody());
}
}
};
public OffBodyTracker() {
mListeners = new HashSet<>();
}
public boolean isOffBody() {
return mIsOffBody && mIsScreenOff;
}
public void register(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_DEVICE_ON_BODY_RECOGNITION);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
context.registerReceiver(mOffBodyReceiver, intentFilter);
}
public void addListener(Listener listener) {
mListeners.add(listener);
}
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
@VisibleForTesting
OffBodyTracker setIsOffBody(boolean isOffBody) {
mIsOffBody = isOffBody;
return this;
}
@VisibleForTesting
OffBodyTracker setIsScreenOff(boolean isScreenOff) {
mIsScreenOff = isScreenOff;
return this;
}
}