Singing Notifier for Android - Source Preview


/*
	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);
            }
        }
    }
}
 
 
back