Skip to content

Commit

Permalink
Add support for hinge sensor state
Browse files Browse the repository at this point in the history
  • Loading branch information
pranavpandey committed May 7, 2022
1 parent d2e42c3 commit b683e33
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.BatteryManager;
import android.os.Build;

Expand All @@ -33,10 +37,12 @@
import com.pranavpandey.android.dynamic.engine.listener.DynamicEventListener;
import com.pranavpandey.android.dynamic.engine.model.DynamicAppInfo;
import com.pranavpandey.android.dynamic.engine.model.DynamicEvent;
import com.pranavpandey.android.dynamic.engine.model.DynamicHinge;
import com.pranavpandey.android.dynamic.engine.model.DynamicPriority;
import com.pranavpandey.android.dynamic.engine.service.DynamicStickyService;
import com.pranavpandey.android.dynamic.engine.task.DynamicAppMonitor;
import com.pranavpandey.android.dynamic.engine.util.DynamicEngineUtils;
import com.pranavpandey.android.dynamic.util.DynamicDeviceUtils;
import com.pranavpandey.android.dynamic.util.DynamicSdkUtils;
import com.pranavpandey.android.dynamic.util.DynamicTaskUtils;

Expand All @@ -55,29 +61,40 @@
* <p>Package must be granted {@link android.Manifest.permission#PACKAGE_USAGE_STATS}
* permission to detect the foreground app on API 21 and above.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
public abstract class DynamicEngine extends DynamicStickyService implements DynamicEventListener {
@TargetApi(Build.VERSION_CODES.R)
public abstract class DynamicEngine extends DynamicStickyService
implements SensorEventListener, DynamicEventListener {

/**
* Intent extra for headset state.
*/
private static final String ADE_EXTRA_HEADSET_STATE = "state";

/**
* Listener to listen special events.
* Sensor manager to register listeners.
*/
private DynamicEventListener mDynamicEventListener;
private SensorManager mSensorManager;

/**
* Broadcast receiver to receive special events.
* Keyguard manager to detect the lock screen state.
*/
private SpecialEventReceiver mSpecialEventReceiver;
private KeyguardManager mKeyguardManager;

/**
* Task to monitor foreground app.
*/
private DynamicAppMonitor mDynamicAppMonitor;

/**
* Broadcast receiver to receive special events.
*/
private SpecialEventReceiver mSpecialEventReceiver;

/**
* The dynamic hinge state.
*/
private @DynamicHinge int mHinge;

/**
* {@code true} if the device is on call. Either ringing or answered.
*/
Expand Down Expand Up @@ -108,11 +125,6 @@ public abstract class DynamicEngine extends DynamicStickyService implements Dyna
*/
private boolean mDocked;

/**
* Keyguard manager to detect the lock screen state.
*/
private KeyguardManager mKeyguardManager;

/**
* Array list to store the events priority.
*/
Expand All @@ -127,15 +139,14 @@ public abstract class DynamicEngine extends DynamicStickyService implements Dyna
public void onCreate() {
super.onCreate();

mDynamicEventListener = this;
mSpecialEventReceiver = new SpecialEventReceiver();
mDynamicAppMonitor = new DynamicAppMonitor(this);
mSensorManager = ContextCompat.getSystemService(this, SensorManager.class);
mKeyguardManager = ContextCompat.getSystemService(this, KeyguardManager.class);

mDynamicAppMonitor = new DynamicAppMonitor(this);
mSpecialEventReceiver = new SpecialEventReceiver();

registerReceiver(mSpecialEventReceiver, DynamicEngineUtils.getEventsIntentFilter());
registerReceiver(mSpecialEventReceiver, DynamicEngineUtils.getPackageIntentFilter());
registerReceiver(mSpecialEventReceiver, DynamicEngineUtils.getCallIntentFilter());

updateEventsPriority();

mEventsMap = new LinkedHashMap<>();
Expand All @@ -146,6 +157,14 @@ public void onCreate() {
* Initialize special events and check for some already occurred and ongoing events.
*/
public void initializeEvents() {
if (DynamicDeviceUtils.hasHingeFeature(this)) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE),
SensorManager.SENSOR_DELAY_NORMAL);
} else {
setHinge(DynamicHinge.UNKNOWN);
}

Intent chargingIntent = registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (chargingIntent != null) {
Expand All @@ -167,7 +186,7 @@ public void initializeEvents() {
!= Intent.EXTRA_DOCK_STATE_UNDOCKED;
}

mDynamicEventListener.onInitialize(mCharging, mHeadset, mDocked);
onInitialize(mCharging, mHeadset, mDocked);
}

/**
Expand All @@ -191,13 +210,22 @@ public void updateEventsMap(@DynamicEvent @NonNull String event, boolean active)
}
}

/**
* Get the sensor manager used by this service.
*
* @return The sensor manager used by this service.
*/
public @NonNull SensorManager getSensorManager() {
return mSensorManager;
}

/**
* Get the listener to listen special events.
*
* @return The listener to listen special events.
*/
public @NonNull DynamicEventListener getSpecialEventListener() {
return mDynamicEventListener;
return this;
}

/**
Expand Down Expand Up @@ -253,11 +281,27 @@ public void onDestroy() {
try {
unregisterReceiver(mSpecialEventReceiver);
setAppMonitorTask(false);

if (DynamicSdkUtils.is30()) {
mSensorManager.unregisterListener(this);
}
} catch (Exception ignored) {
}
super.onDestroy();
}

public @DynamicHinge int getHinge() {
return mHinge;
}

public void setHinge(@DynamicHinge int hinge) {
if (hinge != getHinge()) {
this.mHinge = hinge;

onHingeStateChange(hinge);
}
}

/**
* Get the status of call event.
*
Expand All @@ -277,7 +321,8 @@ public boolean isCall() {
public void setCall(boolean call) {
if (call != isCall()) {
this.mCall = call;
mDynamicEventListener.onCallStateChange(call);

onCallStateChange(call);
}
}

Expand All @@ -298,7 +343,8 @@ public boolean isScreenOff() {
public void setScreenOff(boolean screenOff) {
if (screenOff != isScreenOff()) {
this.mScreenOff = screenOff;
mDynamicEventListener.onScreenStateChange(screenOff);

onScreenStateChange(screenOff);
}
}

Expand All @@ -320,7 +366,8 @@ public boolean isLocked() {
public void setLocked(boolean locked) {
if (locked != isLocked()) {
this.mLocked = locked;
mDynamicEventListener.onLockStateChange(locked);

onLockStateChange(locked);
}
}

Expand All @@ -342,7 +389,8 @@ public boolean isHeadset() {
public void setHeadset(boolean headset) {
if (headset != isHeadset()) {
this.mHeadset = headset;
mDynamicEventListener.onHeadsetStateChange(headset);

onHeadsetStateChange(headset);
}
}

Expand All @@ -361,9 +409,10 @@ public boolean isCharging() {
* @param charging {@code true} if the device is charging or connected to a power source.
*/
public void setCharging(boolean charging) {
if (charging != mCharging) {
if (charging != isCharging()) {
this.mCharging = charging;
mDynamicEventListener.onChargingStateChange(charging);

onChargingStateChange(charging);
}
}

Expand All @@ -384,10 +433,35 @@ public boolean isDocked() {
public void setDocked(boolean docked) {
if (docked != isDocked()) {
this.mDocked = docked;
mDynamicEventListener.onDockStateChange(docked);

onDockStateChange(docked);
}
}

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (DynamicSdkUtils.is30()
&& sensorEvent.sensor.getType() == Sensor.TYPE_HINGE_ANGLE) {
final float[] value = sensorEvent.values;
if (value == null || value.length == 0) {
return;
}

if ((value[0] >= 90 && value[0] < 150) ||
(value[0] > 180 && value[0] <= 270)) {
setHinge(DynamicHinge.HALF_EXPANDED);
} else if ((value[0] >= 150 && value[0] <= 180)
|| (value[0] > 270 && value[0] < 360)) {
setHinge(DynamicHinge.FLAT);
} else {
setHinge(DynamicHinge.COLLAPSED);
}
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int i) { }

/**
* Broadcast receiver to listen various events.
*
Expand Down Expand Up @@ -451,17 +525,15 @@ public void onReceive(@NonNull Context context, @Nullable Intent intent) {
Intent.EXTRA_REPLACING, false);

if (!isReplacing) {
mDynamicEventListener.onPackageRemoved(
intent.getData().getSchemeSpecificPart());
onPackageRemoved(intent.getData().getSchemeSpecificPart());
}
}
break;
case Intent.ACTION_PACKAGE_ADDED:
if (intent.getData() != null
&& intent.getData().getSchemeSpecificPart() != null) {
mDynamicEventListener.onPackageUpdated(
DynamicEngineUtils.getAppInfoFromPackage(context,
intent.getData().getSchemeSpecificPart()), !isReplacing);
onPackageUpdated(DynamicEngineUtils.getAppInfoFromPackage(context,
intent.getData().getSchemeSpecificPart()), !isReplacing);
}
break;
case DynamicEngineUtils.ACTION_ON_CALL:
Expand Down Expand Up @@ -498,6 +570,10 @@ public void onInitialize(boolean charging, boolean headset, boolean docked) {
updateEventsMap(DynamicEvent.DOCK, docked);
}

@CallSuper
@Override
public void onHingeStateChange(@DynamicHinge int state) { }

@CallSuper
@Override
public void onCallStateChange(boolean call) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.pranavpandey.android.dynamic.engine.DynamicEngine;
import com.pranavpandey.android.dynamic.engine.model.DynamicAppInfo;
import com.pranavpandey.android.dynamic.engine.model.DynamicHinge;

/**
* Interface to listen various system events with the help of {@link DynamicEngine}.
Expand All @@ -38,7 +39,16 @@ public interface DynamicEventListener {
void onInitialize(boolean charging, boolean headset, boolean docked);

/**
* This method will be called when call state is changed.
* This method will be called when the hinge state is changed.
*
* @param state The current hinge state.
*
* @see DynamicHinge
*/
void onHingeStateChange(@DynamicHinge int state);

/**
* This method will be called when the call state is changed.
* <p>Either on call or the device is idle.
*
* @param call {@code true} if the device is on call.
Expand All @@ -47,15 +57,15 @@ public interface DynamicEventListener {
void onCallStateChange(boolean call);

/**
* This method will be called when screen state is changed.
* This method will be called when the screen state is changed.
* <p>Either the device screen is off or on.
*
* @param screenOff {@code true} if the device screen is off.
*/
void onScreenStateChange(boolean screenOff);

/**
* This method will be called when lock state is changed.
* This method will be called when the lock state is changed.
* <p>Either the device is in the locked or unlocked state independent of the PIN,
* password or any other security lock.
*
Expand All @@ -64,7 +74,7 @@ public interface DynamicEventListener {
void onLockStateChange(boolean locked);

/**
* This method will be called when headset state is changed.
* This method will be called when the headset state is changed.
* <p>Either the device is connected to a audio output device or volume is routed through
* the internal speaker.
*
Expand All @@ -74,23 +84,23 @@ public interface DynamicEventListener {
void onHeadsetStateChange(boolean connected);

/**
* This method will be called when charging state is changed.
* This method will be called when the charging state is changed.
* <p>Either the device is connected to a power source using the battery.
*
* @param charging {@code true} if the device is charging or connected to a power source.
*/
void onChargingStateChange(boolean charging);

/**
* This method will be called when dock state is changed.
* This method will be called when the dock state is changed.
* <p>Either the device is docked or not.
*
* @param docked {@code true} if the device is docked.
*/
void onDockStateChange(boolean docked);

/**
* This method will be called when foreground app is changed.
* This method will be called when the foreground app is changed.
* <p>Use it to provide the app specific functionality in the app.
*
* @param dynamicAppInfo The dynamic app info of the foreground package.
Expand Down
Loading

0 comments on commit b683e33

Please sign in to comment.