Lunatic/src/net/typeblog/lunatic/Manager/AnimationManager.java

334 lines
12 KiB
Java

/*
* Copyright (C) 2022 Paranoid Android
*
* 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 net.typeblog.lunatic.Manager;
import android.content.Context;
import android.media.audiofx.Visualizer;
import android.os.BatteryManager;
import android.util.Log;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import net.typeblog.lunatic.Constants.Constants;
public final class AnimationManager {
private static final String TAG = "SpotlightAnimationManager";
private static final boolean DEBUG = true;
private Context mContext;
private LEDManager mLEDManager;
private BatteryManager mBatteryManager;
private Visualizer mVisualizer;
public AnimationManager(Context context) {
mContext = context;
mLEDManager = new LEDManager();
}
private static Future<?> submit(Runnable runnable) {
ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
return mExecutorService.submit(runnable);
}
private static boolean check(Constants.SpotlightMode mode) {
if (StatusManager.isCustomEffectActive()) {
if (DEBUG) Log.d(TAG, "Custom effects override all animations | name: " + mode.name());
return false;
}
switch (mode) {
case FLASHLIGHT:
if (!StatusManager.isAllLedsActive())
return false;
break;
case CALLS:
if (StatusManager.isAllLedsActive()) {
if (DEBUG) Log.d(TAG, "All LEDs are active, can\'t start animation | name: " + mode.name());
return false;
}
if (!StatusManager.isCallLedsActive())
return false;
break;
case MUSIC:
if (StatusManager.isAllLedsActive() || StatusManager.isCallLedsActive()) {
if (DEBUG) Log.d(TAG, "Call or All LEDs are active, can\'t start animation | name: " + mode.name());
return false;
}
if (!StatusManager.isMusicLedsActive())
return false;
break;
case NOTIFICATIONS:
if (StatusManager.isAllLedsActive() || StatusManager.isCallLedsActive() || StatusManager.isMusicLedsActive()) {
if (DEBUG) Log.d(TAG, "Call, Music or All LEDs are active, can\'t start animation | name: " + mode.name());
return false;
}
if (!StatusManager.isNotifLedsActive())
return false;
break;
case CHARGING:
if (StatusManager.isAllLedsActive() || StatusManager.isCallLedsActive() || StatusManager.isMusicLedsActive() || StatusManager.isNotifLedsActive()) {
if (DEBUG) Log.d(TAG, "Call, Music, Notification or All LEDs are active, can\'t start animation | name: " + mode.name());
return false;
}
if (!StatusManager.isChargingLedsActive())
return false;
break;
}
return true;
}
private void cleanupAndContinue() {
mLEDManager.enableAllLEDs(false);
if (StatusManager.isAllLedsActive()) {
playFlashlight();
} else if (StatusManager.isCallLedsActive()) {
playCall();
} else if (StatusManager.isMusicLedsActive()) {
playMusic();
} else if (StatusManager.isNotifLedsActive()) {
playNotifications();
} else if (StatusManager.isChargingLedsActive()) {
playCharging();
}
}
private int getBatteryLevel() {
mBatteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
return mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
}
public void playCharging() {
StatusManager.setChargingLedsActive(true);
submit(() -> {
final int num_leds = mLEDManager.getNumLEDs();
int solid_leds = 0;
mLEDManager.enableAllLEDs(false);
mLEDManager.setColor(0xffffff);
mLEDManager.setBrightness((int) (Constants.BRIGHTNESS * 100));
try {
int oldBatteryLevel = -1;
while (check(Constants.SpotlightMode.CHARGING)) {
int batteryLevel = getBatteryLevel();
if (oldBatteryLevel != batteryLevel) {
solid_leds = (int) Math.floor(batteryLevel / 100.0d * num_leds);
for (int i = 0; i < solid_leds; i++) {
mLEDManager.enableLED(num_leds - i - 1, true);
Thread.sleep(150);
}
oldBatteryLevel = batteryLevel;
}
if (100 - solid_leds * mLEDManager.getNumLEDs() > 0) {
mLEDManager.enableLED(num_leds - solid_leds - 1, true);
Thread.sleep(500);
mLEDManager.enableLED(num_leds - solid_leds - 1, false);
Thread.sleep(500);
}
}
} catch (InterruptedException e) {
Log.e(TAG, "Error while playing charging animation", e);
}
mLEDManager.enableAllLEDs(false);
});
}
public void stopCharging() {
if (DEBUG) Log.d(TAG, "Done playing animation | name: charging");
StatusManager.setChargingLedsActive(false);
cleanupAndContinue();
}
public void playCall() {
StatusManager.setCallLedsActive(true);
submit(() -> {
mLEDManager.enableAllLEDs(false);
mLEDManager.setColor(0xffffff);
mLEDManager.setBrightness((int) (Constants.BRIGHTNESS * 100));
try {
boolean enableOdds = false;
while (check(Constants.SpotlightMode.CALLS)) {
for (int i = 0; i < mLEDManager.getNumLEDs(); i++) {
if (i % 2 == (enableOdds ? 1 : 0)) {
mLEDManager.enableLED(i, true);
} else {
mLEDManager.enableLED(i, false);
}
}
enableOdds = !enableOdds;
Thread.sleep(500);
}
} catch (InterruptedException e) {
Log.e(TAG, "Error while playing charging animation", e);
}
mLEDManager.enableAllLEDs(false);
});
}
public void stopCall() {
if (DEBUG) Log.d(TAG, "Disabling Call Animation");
StatusManager.setCallLedsActive(false);
cleanupAndContinue();
}
public void playNotifications() {
StatusManager.setNotifLedsActive(true);
if (DEBUG) Log.d(TAG, "Trying to play notification effect");
submit(() -> {
if (!check(Constants.SpotlightMode.NOTIFICATIONS))
return;
if (DEBUG) Log.d(TAG, "Playing notification effect");
try {
mLEDManager.setBrightness((int) (Constants.BRIGHTNESS * 100));
mLEDManager.setColor(0xffffff);
mLEDManager.enableAllLEDs(true);
Thread.sleep(100);
mLEDManager.enableAllLEDs(false);
Thread.sleep(60);
mLEDManager.enableAllLEDs(true);
Thread.sleep(100);
mLEDManager.enableAllLEDs(false);
} catch (InterruptedException e) {
mLEDManager.enableAllLEDs(false);
}
stopNotifications();
});
}
public void stopNotifications() {
if (DEBUG) Log.d(TAG, "Disabling Notifications Animation");
StatusManager.setNotifLedsActive(false);
cleanupAndContinue();
}
public void playFlashlight() {
StatusManager.setAllLedsActive(true);
submit(() -> {
if (check(Constants.SpotlightMode.FLASHLIGHT)) {
mLEDManager.setColor(0xffffff);
mLEDManager.setBrightness((int) (Constants.BRIGHTNESS * 100));
mLEDManager.enableAllLEDs(true);
}
});
}
public void stopFlashlight() {
if (DEBUG) Log.d(TAG, "Disabling Flashlight");
StatusManager.setAllLedsActive(false);
cleanupAndContinue();
}
private Visualizer.OnDataCaptureListener mVisualizerListener =
new Visualizer.OnDataCaptureListener() {
float rfk, ifk;
float dbValue;
float maxDbValue = 0;
float magnitude;
boolean isAnimating = false;
long lastColorChange = 0;
@Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
if (isAnimating) return;
if (!check(Constants.SpotlightMode.MUSIC))
return;
submit(() -> {
isAnimating = true;
int enabledLEDs = 0;
for (int i = 0; i < mLEDManager.getNumLEDs(); i++) {
rfk = fft[i * 2 + 2]/128.0f;
ifk = fft[i * 2 + 3]/128.0f;
magnitude = rfk * rfk + ifk * ifk;
dbValue = magnitude > 1 ? 1 : magnitude;
if (i == 0) {
// Use the lowest frequency component as the overall brightness
mLEDManager.setBrightness((int) (dbValue * Constants.BRIGHTNESS * 100));
}
int lightId = mLEDManager.getNumLEDs() - i - 1;
// Swap the ring with one of the smaller belts
// so that the ring represents the lowest frequencies
if (lightId == 4) {
lightId = 3;
} else if (lightId == 3) {
lightId = 4;
}
if (dbValue > 0.5) {
enabledLEDs++;
mLEDManager.enableLED(lightId, true);
} else {
mLEDManager.enableLED(lightId, false);
}
}
if (enabledLEDs == 0 && System.currentTimeMillis() - lastColorChange > 1000) {
mLEDManager.setColor(ThreadLocalRandom.current().nextInt(0xffffff));
lastColorChange = System.currentTimeMillis();
}
isAnimating = false;
});
}
};
public void playMusic() {
StatusManager.setMusicLedsActive(true);
try {
mVisualizer = new Visualizer(0);
} catch (Exception e) {
Log.e(TAG, "error initializing visualizer", e);
return;
}
mVisualizer.setEnabled(false);
mVisualizer.setCaptureSize(66);
mVisualizer.setDataCaptureListener(mVisualizerListener, Visualizer.getMaxCaptureRate(),
false, true);
mVisualizer.setEnabled(true);
}
public void stopMusic() {
if (DEBUG) Log.d(TAG, "Disabling Music animation");
StatusManager.setMusicLedsActive(false);
if (mVisualizer != null) {
mVisualizer.setEnabled(false);
mVisualizer.release();
mVisualizer = null;
}
cleanupAndContinue();
}
}