/*
Singing Notifier for Android
Copyright (c) 2020 Linus Suter
Released under the GNU/GPL License:
http://www.gnu.org/licenses/gpl.html
For more information visit:
http://codewelt.com/singer
Android Studio and Android Gradle Plugin: 3.5.2
Gradle: 5.4.1
Compile SDK: 28 (API 28: Android 9.0 (Pie))
Build Tools: 29.0.2
Target SDK and Min SDK: 26 (API 26: Android 8.0 (Oreo))
*/
package com.codewelt.singer;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.EditText;
import android.view.View;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.provider.Settings;
import android.text.TextUtils;
import android.speech.tts.TextToSpeech;
import java.util.Calendar;
import java.util.Locale;
import java.util.ArrayList;
import com.cybozu.labs.langdetect.LinusDetector;
import com.cybozu.labs.langdetect.LinusDetectorFactory;
import com.cybozu.labs.langdetect.Language;
public class MainActivity extends AppCompatActivity {
private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
private ChangeBroadcastReceiver changeBroadcastReceiver;
private TextToSpeech tts;
private LinusDetector detector;
private int msInt = 1600;
private int count = 0;
private int countPercent = 0;
private String repeatSaveText = "Linus";
private Locale repeatSaveLocale = Locale.US;
private boolean locked = true;
private boolean once = true;
private float ttsPitch = 1.0f;
private float ttsSpeechRate = 1.0f;
private Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
runnable = new Runnable() {
@Override
public void run() {
detector = LinusDetectorFactory.create();
TextView countPercent = findViewById(R.id.textViewCount);
countPercent.setTextColor(0xFF009400);
countPercent.setText(R.string.ready);
Switch viewOne = findViewById(R.id.viewOneSwitch);
viewOne.setEnabled(true);
Switch switchPrivacy = findViewById(R.id.switchPrivacy);
switchPrivacy.setEnabled(true);
Switch switchLanguage = findViewById(R.id.switchDetect);
switchLanguage.setEnabled(true);
Switch name = findViewById(R.id.switchName);
name.setEnabled(true);
Switch viewSleep = findViewById(R.id.switchSleep);
viewSleep.setEnabled(true);
EditText delay = findViewById(R.id.editTextDelay);
delay.setEnabled(true);
EditText pitch = findViewById(R.id.editTextPitch);
pitch.setEnabled(true);
EditText rate = findViewById(R.id.editTextRate);
rate.setEnabled(true);
Button buttonRepeatSpeech = findViewById(R.id.buttonRepeat);
buttonRepeatSpeech.setEnabled(true);
Button silenceButton = findViewById(R.id.viewSilenceButton);
silenceButton.setEnabled(true);
Button quitButton = findViewById(R.id.viewQuitButton);
quitButton.setEnabled(true);
locked = false;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
Switch viewOne = findViewById(R.id.viewOneSwitch);
viewOne.setClickable(true);
Switch switchPrivacy = findViewById(R.id.switchPrivacy);
switchPrivacy.setClickable(true);
Switch switchLanguage = findViewById(R.id.switchDetect);
switchLanguage.setClickable(true);
Switch name = findViewById(R.id.switchName);
name.setClickable(true);
Switch viewSleep = findViewById(R.id.switchSleep);
viewSleep.setClickable(true);
EditText delay = findViewById(R.id.editTextDelay);
delay.setClickable(true);
EditText pitch = findViewById(R.id.editTextPitch);
pitch.setClickable(true);
EditText rate = findViewById(R.id.editTextRate);
rate.setClickable(true);
Button buttonRepeatSpeech = findViewById(R.id.buttonRepeat);
buttonRepeatSpeech.setClickable(true);
Button silenceButton = findViewById(R.id.viewSilenceButton);
silenceButton.setClickable(true);
Button quitButton = findViewById(R.id.viewQuitButton);
quitButton.setClickable(true);
}
}, 44);
}
};
if(!isNotificationServiceEnabled()) {
AlertDialog.Builder alertDialogBuilderStart = new AlertDialog.Builder(this);
alertDialogBuilderStart.setTitle(R.string.app_name);
alertDialogBuilderStart.setMessage(R.string.please);
alertDialogBuilderStart.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
startActivityForResult(new Intent(android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 33);
}
});
alertDialogBuilderStart.create();
alertDialogBuilderStart.show();
}
changeBroadcastReceiver = new ChangeBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.codewelt.singer");
registerReceiver(changeBroadcastReceiver,intentFilter);
tts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) {
tts.setLanguage(Locale.US);
}
}
});
Button buttonRepeatSpeech = findViewById(R.id.buttonRepeat);
buttonRepeatSpeech.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EditText pitch = findViewById(R.id.editTextPitch);
String pitchValue = pitch.getText().toString();
ttsPitch = Float.parseFloat(pitchValue);
if (ttsPitch > 4.0f) {
ttsPitch = 4.0f;
pitch.setText(String.valueOf(ttsPitch));
}
if (ttsPitch < 0.1f) {
ttsPitch = 0.1f;
pitch.setText(String.valueOf(ttsPitch));
}
EditText rate = findViewById(R.id.editTextRate);
String rateValue = rate.getText().toString();
ttsSpeechRate = Float.parseFloat(rateValue);
if (ttsSpeechRate > 4.0f) {
ttsSpeechRate = 4.0f;
rate.setText(String.valueOf(ttsSpeechRate));
}
if (ttsSpeechRate < 0.1f) {
ttsSpeechRate = 0.1f;
rate.setText(String.valueOf(ttsSpeechRate));
}
tts.stop();
tts.setPitch(ttsPitch);
tts.setSpeechRate(ttsSpeechRate);
if (repeatSaveText.equals("Linus")) {
tts.setLanguage(Locale.US);
tts.speak("Singing Notifier. Waiting for Notifications.", TextToSpeech.QUEUE_FLUSH, null, "utter");
} else {
TextView viewCount = findViewById(R.id.textViewCount);
viewCount.setTextColor(Color.BLACK);
if (count > 1) viewCount.setText("Counting " + String.valueOf(count) + " Notifications.");
else viewCount.setText(R.string.countingOne);
tts.setLanguage(repeatSaveLocale);
tts.speak(repeatSaveText, TextToSpeech.QUEUE_FLUSH, null, "utter");
}
}
});
Button silenceButton = findViewById(R.id.viewSilenceButton);
silenceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(tts != null){
tts.stop();
}
}
});
Button quitButton = findViewById(R.id.viewQuitButton);
quitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finishAndRemoveTask();
System.exit(0);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 33) {
if (locked) {
once = false;
new Handler(Looper.getMainLooper()).postDelayed(runnable, 123);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (tts != null) {
tts.stop();
tts.shutdown();
}
unregisterReceiver(changeBroadcastReceiver);
}
@Override
public void onWindowFocusChanged(boolean focus) {
super.onWindowFocusChanged(focus);
if (locked && once) {
once = false;
new Handler(Looper.getMainLooper()).postDelayed(runnable, 123);
}
}
private boolean isNotificationServiceEnabled() {
String pkgName = getPackageName();
final String flat = Settings.Secure.getString(getContentResolver(), ENABLED_NOTIFICATION_LISTENERS);
if (!TextUtils.isEmpty(flat)) {
final String[] names = flat.split(":");
for (int i = 0; i < names.length; i++) {
final ComponentName cn = ComponentName.unflattenFromString(names[i]);
if (cn != null) {
if (TextUtils.equals(pkgName, cn.getPackageName())) {
return true;
}
}
}
}
return false;
}
public class ChangeBroadcastReceiver extends BroadcastReceiver {
Language language;
String receivedNotificationString;
String[] receivedNotificationStringArray;
@Override
public void onReceive(Context context, Intent intent) {
Switch viewOne = findViewById(R.id.viewOneSwitch);
if (!viewOne.isChecked() || locked) return;
if (!isNotificationServiceEnabled()) {
TextView viewCountService = findViewById(R.id.textViewCount);
viewCountService.setTextColor(Color.RED);
viewCountService.setText(R.string.enableFirst);
viewOne.setChecked(false);
} else {
receivedNotificationString = intent.getStringExtra("Notification Data");
// Charging Skip
if (receivedNotificationString.contains("%")) countPercent++;
else if (countPercent > 0 && receivedNotificationString.contains(" h") && receivedNotificationString.contains(" min")) countPercent++;
else countPercent = 0;
if (countPercent > 1) {
TextView viewCountPercent = findViewById(R.id.textViewCount);
viewCountPercent.setTextColor(0xFFFFC800);
viewCountPercent.setText(R.string.charging);
return;
}
detector = LinusDetectorFactory.create();
receivedNotificationStringArray = receivedNotificationString.split("###!!!###");
detector.append(receivedNotificationStringArray[2]);
try {
ArrayList<Language> tre = detector.getProbabilities();
language = tre.get(0);
} catch (Exception e) {
// null
}
EditText delay = findViewById(R.id.editTextDelay);
String ms = delay.getText().toString();
msInt = Integer.parseInt(ms);
if (msInt > 3000) {
msInt = 3000;
delay.setText(String.valueOf(msInt));
}
if (msInt < 1100) {
msInt = 1100;
delay.setText(String.valueOf(msInt));
}
EditText pitch = findViewById(R.id.editTextPitch);
String pitchValue = pitch.getText().toString();
ttsPitch = Float.parseFloat(pitchValue);
if (ttsPitch > 4.0f) {
ttsPitch = 4.0f;
pitch.setText(String.valueOf(ttsPitch));
}
if (ttsPitch < 0.1f) {
ttsPitch = 0.1f;
pitch.setText(String.valueOf(ttsPitch));
}
EditText rate = findViewById(R.id.editTextRate);
String rateValue = rate.getText().toString();
ttsSpeechRate = Float.parseFloat(rateValue);
if (ttsSpeechRate > 4.0f) {
ttsSpeechRate = 4.0f;
rate.setText(String.valueOf(ttsSpeechRate));
}
if (ttsSpeechRate < 0.1f) {
ttsSpeechRate = 0.1f;
rate.setText(String.valueOf(ttsSpeechRate));
}
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
String speakString = "Suter";
if (receivedNotificationStringArray[1].equals(receivedNotificationStringArray[2])) {
speakString = receivedNotificationStringArray[1];
} else {
Switch switchPrivacy = findViewById(R.id.switchPrivacy);
if (switchPrivacy.isChecked()) speakString = receivedNotificationStringArray[1];
else speakString = receivedNotificationStringArray[1] + ", " + receivedNotificationStringArray[2];
}
Switch name = findViewById(R.id.switchName);
if (name.isChecked()) {
String packageName = receivedNotificationStringArray[0];
packageName = packageName.replaceFirst("com.", "");
packageName = packageName.replaceFirst("net.", "");
packageName = packageName.replaceFirst("org.", "");
packageName = packageName.replaceFirst("samsung.", "");
packageName = packageName.replaceFirst("google.", "");
packageName = packageName.replaceFirst("android.", "");
packageName = packageName.replaceFirst("android", "");
packageName = packageName.replaceFirst("sec.", "");
speakString = packageName + ", " + speakString;
}
TextView viewCount = findViewById(R.id.textViewCount);
if (repeatSaveText == speakString) {
if (count > 1) viewCount.setText("Counting " + String.valueOf(count) + " Notifications. Duplicate.");
else viewCount.setText(R.string.countingOneDuplicate);
return;
}
tts.stop();
tts.setPitch(ttsPitch);
tts.setSpeechRate(ttsSpeechRate);
Locale detectedLocale;
Switch switchLanguage = findViewById(R.id.switchDetect);
if (switchLanguage.isChecked()) detectedLocale = Locale.forLanguageTag(language.lang);
else detectedLocale = Locale.US;
int setLangResult = tts.setLanguage(detectedLocale);
if (setLangResult == TextToSpeech.LANG_MISSING_DATA) {
tts.setLanguage(Locale.US);
detectedLocale = Locale.US;
speakString = "Language Data missing.";
} else if (setLangResult == TextToSpeech.LANG_NOT_SUPPORTED) {
// Fallback
tts.setLanguage(Locale.US);
detectedLocale = Locale.US;
}
Switch viewSleep = findViewById(R.id.switchSleep);
Calendar cal = Calendar.getInstance();
int hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
if ((hourOfDay > 21 || hourOfDay < 10) && viewSleep.isChecked()) {
repeatSaveText = speakString;
repeatSaveLocale = detectedLocale;
viewCount.setTextColor(Color.BLUE);
viewCount.setText(R.string.sleeping);
} else {
tts.speak(speakString, TextToSpeech.QUEUE_FLUSH, null, "utter");
repeatSaveText = speakString;
repeatSaveLocale = detectedLocale;
count++;
viewCount.setTextColor(Color.BLACK);
if (count > 1) viewCount.setText("Counting " + String.valueOf(count) + " Notifications.");
else viewCount.setText(R.string.countingOne);
}
}
}, msInt + 123);
}
}
}
}