diff --git a/README.md b/README.md index fe8e612..7a9415e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ -# android-infinite-timer +# Infinite Timer -Timer which loops over the specified interval, playing your default notification sound each time it hits 0. \ No newline at end of file +Timer which loops over the specified interval, playing your default notification sound each time it hits 0. + +## Update Notes + +### 2025-07-11 +(This article)[https://sijus.medium.com/resurrecting-a-5-year-old-android-app-a-developers-journey-59d8f5689e5b] + was very helpful in getting this app updated from SDK 25 to 36. + +## Licenses +Chime sound byte came free from here: +https://pixabay.com/sound-effects/chime-sound-7143/ diff --git a/app/build.gradle b/app/build.gradle index 21a9460..e5b53a3 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + namespace "com.hyperling.apps.infinitetimer" + compileSdkVersion 36 defaultConfig { applicationId "com.hyperling.apps.infinitetimer" - minSdkVersion 15 - targetSdkVersion 28 - versionCode 7 - versionName "1.06" + minSdkVersion 14 + targetSdkVersion 36 + versionCode 8 + versionName "1.1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/androidTest/java/com/hyperling/apps/infinitetimer/deleteme/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/hyperling/apps/infinitetimer/deleteme/ExampleInstrumentedTest.java deleted file mode 100755 index 068a20c..0000000 --- a/app/src/androidTest/java/com/hyperling/apps/infinitetimer/deleteme/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hyperling.apps.infinitetimer.deleteme; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("infinitetimer.apps.hyperling.com.infinitetimer", appContext.getPackageName()); - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0f3a62d..72bd899 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,18 +1,17 @@ - + - + - diff --git a/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java b/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java index ed37980..05f5547 100755 --- a/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java +++ b/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java @@ -1,28 +1,35 @@ package com.hyperling.apps.infinitetimer; +import android.app.NotificationManager; +import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Color; +import android.media.AudioManager; import android.media.MediaPlayer; +import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; +import android.view.KeyEvent; import android.view.View; -import android.view.ViewManager; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; +import android.widget.SeekBar; import android.widget.TableRow; import android.widget.TextView; +import java.net.URI; import java.util.Map; public class MainActivity extends AppCompatActivity { - String TAG, + String TAG = "MainActivity.", keySharedPreferences, keyDebug, keyChronometerTime, keyLoopInterval, keyStartStop, keyAppOpen, keyServiceRunning, stringChronometerDefault, stringChronometerTime, stringLoopInterval; @@ -47,8 +54,15 @@ public class MainActivity extends AppCompatActivity { Uri ringtoneUri; MediaPlayer mediaPlayer; + SeekBar seekBar; + AudioManager audioManager; + TextView tvSeekBar; + @Override protected void onCreate(Bundle savedInstanceState) { + String tag = "onCreate"; + if (DEBUG) Log.d(tag, "Starting"); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); @@ -56,7 +70,6 @@ public class MainActivity extends AppCompatActivity { this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); /***** Get Strings *****/ - TAG = getString(R.string.TAG); keySharedPreferences = getString(R.string.keySharedPreferences); keyDebug = getString(R.string.keyDebug); keyChronometerTime = getString(R.string.keyChronometerTime); @@ -67,36 +80,83 @@ public class MainActivity extends AppCompatActivity { stringChronometerDefault = getString(R.string.chronometerDefault); + if (DEBUG) Log.d(tag, "Got strings."); + /***** Shared Preferences *****/ sharedPreferences = getSharedPreferences(keySharedPreferences, MODE_PRIVATE); - // TODO: Comment these lines! - //sharedPreferences.edit().putBoolean(keyDebug, true).apply(); - //sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); - //sharedPreferences.edit().putString(keyLoopInterval, stringChronometerDefault).apply(); - //sharedPreferences.edit().putString(keyChronometerTime, stringChronometerDefault).apply(); - // TODO: Keep these! - DEBUG = sharedPreferences.getBoolean(keyDebug, false); + DEBUG = sharedPreferences.getBoolean(keyDebug, true); appOpen = sharedPreferences.getBoolean(keyAppOpen, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); - /* - if (!appOpen && !serviceRunning) { - sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); - } - */ + if (DEBUG) Log.d(tag, "Got preferences."); // Sound ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + if (DEBUG) Log.d(tag, "ringtoneUri=" + ringtoneUri.toString()); mediaPlayer = MediaPlayer.create(this, ringtoneUri); + if (mediaPlayer == null) { + if (DEBUG) Log.d(tag, "Media Player is null, trying alarm"); + ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); + mediaPlayer = MediaPlayer.create(this, ringtoneUri); + } + if (mediaPlayer == null) { + if (DEBUG) Log.d(tag, "Media Player is null, trying ringtone"); + ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + mediaPlayer = MediaPlayer.create(this, ringtoneUri); + } + if (mediaPlayer == null) { + if (DEBUG) Log.d(tag, "Media Player is null, trying all"); + ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALL); + mediaPlayer = MediaPlayer.create(this, ringtoneUri); + } + if (mediaPlayer == null) { + if (DEBUG) Log.d(tag, "Media Player is null, trying local sound byte"); + mediaPlayer = MediaPlayer.create(this, R.raw.chime_sound_7143); + } + if (mediaPlayer == null) { + String message = "Could not find a notification, alarm, or ringtone to play."; + message += " App will not be able to function without one of these."; + message += " Please exit and install a system notification sound."; - if (DEBUG) Log.d(TAG, "MainActivity.onCreate! Got strings."); + new AlertDialog.Builder(MainActivity.this) + .setTitle("Error") + .setMessage(message) + .setPositiveButton("Exit", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + finish(); + } + }) + .setCancelable(false) + .show() + ; + } else { + if (DEBUG) Log.d(tag, "mediaPlayer=" + mediaPlayer.toString()); + } if (DEBUG){ // Print all preferences Map sharedPreferencesAll = sharedPreferences.getAll(); for (String key : sharedPreferencesAll.keySet()){ - Log.d(TAG, "MainActivity.onCreate! key=" + key + " value=" + sharedPreferencesAll.get(key)); + String value = sharedPreferencesAll.get(key).toString(); + Log.d(tag, "key=" + key + " value=" + value); + + // If app crashed, ensure buttons are correct. + if (key.equals(keyStartStop) && value.equals("true")) { + if (DEBUG) Log.d(tag, "Shutting off improper START value"); + + // Ensure START is always off if we are creating (not resuming). + int count = 0; + while (!sharedPreferences.edit().putBoolean(keyStartStop, false).commit() && count < 50) { + if (DEBUG) Log.d(tag, "Commit failed, trying again. count=" + count); + count++; + } + + boolean checkStartStop = sharedPreferences.getBoolean(keyStartStop, false);; + Log.d(tag, "key=" + key + " value=" + checkStartStop); + } } } @@ -150,21 +210,54 @@ public class MainActivity extends AppCompatActivity { etMillis.setVisibility(View.GONE); //(ViewManager) etMillis.getParent().remove - //sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); + seekBar = findViewById(R.id.seekBar); + setVolumeControlStream(AudioManager.STREAM_MUSIC); + + audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + seekBar.setMax(maxVolume); + + tvSeekBar = findViewById(R.id.tvSeekbar); + + syncVolume(); + + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int i, boolean b) { + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, i, AudioManager.FLAG_SHOW_UI); + syncVolume(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + syncVolume(); + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + syncVolume(); + } + }); + + recoverScreen(); - if (DEBUG) Log.d(TAG, "MainActivity.onCreate! Finished."); + if (DEBUG) Log.d(tag, "Finished"); } private void doTimeChooser(){ - if (DEBUG) Log.d(TAG, "MainActivity.openTimeChooser!"); + String tag = TAG + "doTimeChooser"; + if (DEBUG) Log.d(tag, "Starting"); //stringLoopInterval = "00:00:07"; //setEditTexts(stringLoopInterval); + + if (DEBUG) Log.d(tag, "Finished"); } private void setEditTexts(String interval){ - if (DEBUG) Log.d(TAG, "MainActivity.setEditTexts! interval=" + interval); + String tag = TAG + "setEditTexts"; + if (DEBUG) Log.d(tag, "Starting, interval=" + interval); if (!interval.equals(stringChronometerDefault)) { String[] time = interval.split(":"); @@ -188,29 +281,67 @@ public class MainActivity extends AppCompatActivity { } setLoopInterval(); + if (DEBUG) Log.d(tag, "Finished"); } private void setLoopInterval(){ - if (DEBUG) Log.d(TAG, "MainActivity.setLoopInterval!"); + String tag = TAG + "setLoopInterval"; + if (DEBUG) Log.d(tag, "Starting"); - int hours = Integer.parseInt("0"+etHours.getText().toString()); - int minutes = Integer.parseInt("0"+etMinutes.getText().toString()); - int seconds = Integer.parseInt("0"+etSeconds.getText().toString()); + int hours = 0, minutes = 0, seconds = 0; + try { + hours = Integer.parseInt("0" + etHours.getText().toString()); + } catch (Exception e) { + if (DEBUG) Log.d(tag, "Hours had an exception, using highest value"); + hours = 99; + } + try { + minutes = Integer.parseInt("0" + etMinutes.getText().toString()); + } catch (Exception e) { + if (DEBUG) Log.d(tag, "Minutes had an exception, using highest value"); + minutes = 59; + } + try { + seconds = Integer.parseInt("0" + etSeconds.getText().toString()); + } catch (Exception e) { + if (DEBUG) Log.d(tag, "Seconds had an exception, using highest value"); + seconds = 59; + } + + if ((hours + (minutes/60) + (seconds/60/60)) > 99) { + if (DEBUG) Log.d(tag, "Sum is over 99 hours, setting to 99:59:59"); + hours = 99; + minutes = 59; + seconds = 59; + } + + if (DEBUG) Log.d(tag, "hours=" + hours); + if (DEBUG) Log.d(tag, "minutes=" + minutes); + if (DEBUG) Log.d(tag, "seconds=" + seconds); + + if (hours > 0) etHours.setText("" + hours); + if (minutes > 0) etMinutes.setText("" + minutes); + if (seconds > 0) etSeconds.setText("" + seconds); longLoopInterval = (((hours*60*60) + (minutes*60) + seconds) * 1000); - if (DEBUG) Log.d(TAG, "MainActivity.setLoopInterval! longLoopInterval=" + longLoopInterval); + if (DEBUG) Log.d(tag, "longLoopInterval=" + longLoopInterval); setChronometer(longLoopInterval); + + if (DEBUG) Log.d(tag, "Finished"); } private void setChronometer(long milliseconds){ - if (DEBUG) Log.d(TAG, "MainActivity.setChronometer! milliseconds=" + milliseconds); + String tag = TAG + "setChronometer"; + if (DEBUG) Log.d(tag, "Starting, milliseconds=" + milliseconds); //chronometer = (TextView) findViewById(R.id.chronometer); - String[] time = {Long.toString(milliseconds/(1000*60*60)), - Long.toString((milliseconds/(1000*60)%(60*60))), - Long.toString((milliseconds/(1000))%60)}; + String[] time = { + Long.toString(milliseconds/(1000*60*60)), + Long.toString((milliseconds/(1000*60))%60), + Long.toString((milliseconds/(1000))%60) + }; for (int i = 0; i < time.length; i++){ while (time[i].length() < 2){ @@ -229,16 +360,18 @@ public class MainActivity extends AppCompatActivity { doTimeChooser(); } });*/ - if (DEBUG) Log.d(TAG, "MainActivity.setChronometer! display=" + display); + if (DEBUG) Log.d(tag, "display=" + display); sharedPreferences.edit().putString(keyChronometerTime, display).apply(); - if (DEBUG) Log.d(TAG, "MainActivity.setChronometer! Before=" + chronometer.getText()); + if (DEBUG) Log.d(tag, "Before=" + chronometer.getText()); chronometer.setText(display); - if (DEBUG) Log.d(TAG, "MainActivity.setChronometer! After=" + chronometer.getText()); + if (DEBUG) Log.d(tag, "After=" + chronometer.getText()); + if (DEBUG) Log.d(tag, "Finished"); } private void startStopChronometer(String buttonText){ - if (DEBUG) Log.d(TAG, "MainActivity.startStopChronometer!"); + String tag = TAG + "startStopChronometer"; + if (DEBUG) Log.d(tag, "Starting"); start = !start; SharedPreferences.Editor editor = sharedPreferences.edit(); @@ -274,10 +407,13 @@ public class MainActivity extends AppCompatActivity { flipStartStopButton(); flipResetPauseButton(); + + if (DEBUG) Log.d(tag, "Finished"); } private void recoverScreen(){ - if (DEBUG) Log.d(TAG, "MainActivity.recoverScreen!"); + String tag = TAG + "recoverScreen"; + if (DEBUG) Log.d(tag, "Starting"); /***** Retrieve the last settings *****/ // Chronometer @@ -290,6 +426,7 @@ public class MainActivity extends AppCompatActivity { // Start/Stop Button start = sharedPreferences.getBoolean(keyStartStop, false); + if (DEBUG) Log.d(tag, "start=" + start); flipStartStopButton(); flipResetPauseButton(); @@ -301,10 +438,13 @@ public class MainActivity extends AppCompatActivity { //startStopChronometer(stringChronometerTime); //incrementTime.sendEmptyMessage(0); + + if (DEBUG) Log.d(tag, "Finished"); } private void flipStartStopButton(){ - if (DEBUG) Log.d(TAG, "MainActivity.flipStartStopButton!"); + String tag = TAG + "flipStartStopButton"; + if (DEBUG) Log.d(tag, "Starting"); start = sharedPreferences.getBoolean(keyStartStop, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); @@ -319,10 +459,13 @@ public class MainActivity extends AppCompatActivity { } btnStartStop.setBackgroundColor(Color.GREEN); } + + if (DEBUG) Log.d(tag, "Finished"); } private void resetPauseChronometer(){ - if (DEBUG) Log.d(TAG, "MainActivity.resetPauseChronometer!"); + String tag = TAG + "resetPauseChronometer"; + if (DEBUG) Log.d(tag, "Starting"); start = sharedPreferences.getBoolean(keyStartStop, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); @@ -333,10 +476,13 @@ public class MainActivity extends AppCompatActivity { else{ setEditTexts(stringChronometerDefault); } + + if (DEBUG) Log.d(tag, "Finished"); } private void flipResetPauseButton(){ - if (DEBUG) Log.d(TAG, "MainActivity.flipResetPauseButton!"); + String tag = TAG + "flipResetPauseButton"; + if (DEBUG) Log.d(tag, "Starting"); start = sharedPreferences.getBoolean(keyStartStop, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); @@ -349,13 +495,17 @@ public class MainActivity extends AppCompatActivity { btnResetPause.setText(getString(R.string.btnReset)); btnResetPause.setBackgroundColor(Color.RED); } + + if (DEBUG) Log.d(tag, "Finished"); } - private Handler incrementTime = new Handler(){ + private final Handler incrementTime = new Handler(){ @Override public void handleMessage(Message msg) { + String tag = TAG + "handleMessage"; + if (DEBUG) Log.d(tag, "Starting"); + super.handleMessage(msg); - if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage!"); appOpen = sharedPreferences.getBoolean(keyAppOpen, true); if (appOpen) { @@ -372,7 +522,7 @@ public class MainActivity extends AppCompatActivity { // Calculate new time and text setTimeLeft(); - if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage! longTimeLeft=" + longTimeLeft); + if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft); // Check if we need to beep if (longTimeLeft < 1000){ @@ -381,12 +531,12 @@ public class MainActivity extends AppCompatActivity { setStartTime(); if (DEBUG) { - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longLoopInterval=" + longLoopInterval); - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longStartTime=" + longStartTime); - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longEndTime=" + longEndTime); - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longTimeLeft=" + longTimeLeft); - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C waitInterval=" + waitInterval); - Log.d(TAG, "MainActivity.incrementTime.handleMessage!C start=" + start); + Log.d(tag, "longLoopInterval=" + longLoopInterval); + Log.d(tag, "longStartTime=" + longStartTime); + Log.d(tag, "longEndTime=" + longEndTime); + Log.d(tag, "longTimeLeft=" + longTimeLeft); + Log.d(tag, "waitInterval=" + waitInterval); + Log.d(tag, "start=" + start); //chronometer.setText("Test"); //setChronometer(200); /* @@ -404,11 +554,11 @@ public class MainActivity extends AppCompatActivity { synchronized (this){ try{ wait(waitInterval); - if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage! Done waiting."); + if (DEBUG) Log.d(tag, "Done waiting."); } catch (Exception e){ e.printStackTrace(); - if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage! Failed to wait."); + if (DEBUG) Log.d(tag, "Failed to wait."); } } //sharedPreferences.edit().putBoolean(keyStartStop, true).apply(); @@ -419,59 +569,146 @@ public class MainActivity extends AppCompatActivity { Thread t = new Thread(task); t.start(); } + if (DEBUG) Log.d(tag, "Finished"); } }; private void setStartTime(){ - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime!"); + String tag = TAG + "setStartTime"; + if (DEBUG) Log.d(tag, "Starting"); + if (DEBUG) Log.d(tag, "System.currentTimeMillis()=" + System.currentTimeMillis()); if (longTimeLeft == 0) { - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longTimeLeft==" + longTimeLeft); + if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft); // Add extra 1000 so timer starts on interval and beeps after 1 longStartTime = System.currentTimeMillis() + 1000 - waitInterval; - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longStartTime=" + longStartTime); + if (DEBUG) Log.d(tag, "longStartTime=" + longStartTime); longEndTime = longStartTime + longLoopInterval; - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longEndTime=" + longEndTime); - } - else{ - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longTimeLeft==" + longTimeLeft); + if (DEBUG) Log.d(tag, "longEndTime=" + longEndTime); + } else { + if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft); longStartTime = System.currentTimeMillis() - waitInterval; - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longStartTime=" + longStartTime); + if (DEBUG) Log.d(tag, "longStartTime=" + longStartTime); longEndTime = longStartTime + longTimeLeft; - if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longEndTime=" + longEndTime); + if (DEBUG) Log.d(tag, "longEndTime=" + longEndTime); } + + if (DEBUG) Log.d(tag, "Finished"); } private void setTimeLeft(){ - if (DEBUG) Log.d(TAG, "MainActivity.setTimeLeft!"); + String tag = TAG + "setTimeLeft"; + if (DEBUG) Log.d(tag, "Starting"); longTimeLeft = longEndTime - System.currentTimeMillis() + waitInterval; - if (DEBUG) Log.d(TAG, "MainActivity.setTimeLeft! longTimeLeft=" + longTimeLeft); + if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft); setChronometer(longTimeLeft); + + if (DEBUG) Log.d(tag, "Finished"); } private void resetTimeLeft(){ - if (DEBUG) Log.d(TAG, "MainActivity.resetTimeLeft!"); + String tag = TAG + "resetTimeLeft"; + if (DEBUG) Log.d(tag, "Starting"); + longTimeLeft = 0; setChronometer(longLoopInterval); + + if (DEBUG) Log.d(tag, "Finished"); } private void playSound(){ - if (DEBUG) Log.d(TAG, "MainActivity.playSound!"); + String tag = TAG + "playSound"; + if (DEBUG) Log.d(tag, "Starting"); - /* Original code, did not work on Nexus 7 with CM 12.1-20151117 (Android 5.1.1) - Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri); + /* Original code, did not work on Nexus 7 with CM 12.1-20151117 (Android 5.1.1) * / + Uri uri = Uri.parse("android.resource://" + getPackageName() + "/raw/chime_sound_7143"); + //Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri); + Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), uri); ringtone.play(); - */ + /**/ - //mediaPlayer.reset(); - mediaPlayer.start(); + try { + mediaPlayer.start(); + } catch (Exception e) { + sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); + + String message = "Failed to play a notification sound."; + message += " Please screenshot this error and send it to me@hyperling.com:\n\n"; + message += e.toString(); + + new AlertDialog.Builder(MainActivity.this) + .setTitle("Error") + .setMessage(message) + .setPositiveButton("Exit", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + finish(); + } + }) + .setCancelable(false) + .show() + ; + } + + if (DEBUG) Log.d(tag, "Finished"); + } + + private void flipInputTexts() { + String tag = TAG + "flipInputTexts"; + if (DEBUG) Log.d(tag, "Starting"); + + etHours.setEnabled(!start); + etMinutes.setEnabled(!start); + etSeconds.setEnabled(!start); + + if (DEBUG) Log.d(tag, "Finished"); + } + + private void resetAll() { + String tag = TAG + "resetAll"; + if (DEBUG) Log.d(tag, "Starting"); + + if (DEBUG) Log.d(tag, "Resetting screen values..."); + setChronometer(0); + setEditTexts(""); + setLoopInterval(); + if (DEBUG) Log.d(tag, "Values reset"); + + if (DEBUG) Log.d(tag, "Resetting SharedPreferences..."); + start = false; + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(keyStartStop, start); + editor.putBoolean(keyAppOpen, start); + editor.putBoolean(keyServiceRunning, start); + int count = 0; + while (!editor.commit() && count < 50) { + Log.i(tag, "Failed! Reattempting commit... count=" + count); + count++; + }; + if (DEBUG) Log.d(tag, "SharedPreferences reset"); + + if (DEBUG) Log.d(tag, "Finished"); + } + + public void syncVolume() { + String tag = TAG + "syncVolume"; + if (DEBUG) Log.d(tag, "Starting"); + + int currVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + seekBar.setProgress(currVolume); + + String volume = getString(R.string.tvSeekBar) + seekBar.getProgress() + "/" + seekBar.getMax(); + tvSeekBar.setText(volume); + + if (DEBUG) Log.d(tag, "Finished"); } @Override protected void onPause() { - super.onPause(); - if (DEBUG) Log.d(TAG, "MainActivity.onPause!"); + String tag = TAG + "onPause"; + if (DEBUG) Log.d(tag, "Starting"); stringLoopInterval = etHours.getText().toString() + ":" + @@ -483,22 +720,50 @@ public class MainActivity extends AppCompatActivity { editor.putString(keyLoopInterval, stringLoopInterval); editor.putBoolean(keyAppOpen, false); editor.apply(); + + if (DEBUG) Log.d(tag, "Done with local code, calling super."); + + super.onPause(); + + if (DEBUG) Log.d(tag, "Finished"); } @Override protected void onResume() { super.onResume(); - if (DEBUG) Log.d(TAG, "MainActivity.onResume!"); + + String tag = TAG + "onResume"; + if (DEBUG) Log.d(tag, "Starting"); sharedPreferences.edit().putBoolean(keyServiceRunning, false).apply(); recoverScreen(); + + if (DEBUG) Log.d(tag, "Finished"); } - private void flipInputTexts() { - etHours.setEnabled(!start); - etMinutes.setEnabled(!start); - etSeconds.setEnabled(!start); + @Override + protected void onDestroy() { + String tag = TAG + "onDestroy"; + if (DEBUG) Log.d(tag, "Starting"); + + resetAll(); + + if (DEBUG) Log.d(tag, "Done with local code, calling super.."); + + super.onDestroy(); + + if (DEBUG) Log.d(tag, "Finished"); } -} + // TODO: This is somehow always 1 action behind the actual volume when pressing volume buttons. + // Like when doing down+up+down, the bar does nothing+down+up. + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + boolean bool = super.onKeyDown(keyCode, event); + syncVolume(); + return bool; + } + return super.onKeyDown(keyCode, event); + }} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f7238ed..d30d6ed 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,7 @@ android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginTop="?android:actionBarSize" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" @@ -157,4 +158,27 @@ + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index ddf5d9e..a7af6f4 100755 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -5,20 +5,18 @@ android:id="@+id/action_milis" android:orderInCategory="100" android:title="@string/action_millis" - app:showAsAction="never" android:checkable="true" /> + /> \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_512.png b/app/src/main/res/mipmap/icon_512.png similarity index 100% rename from app/src/main/res/drawable/icon_512.png rename to app/src/main/res/mipmap/icon_512.png diff --git a/app/src/main/res/mipmap/icon_rounded.png b/app/src/main/res/mipmap/icon_rounded.png new file mode 100644 index 0000000..70a529e Binary files /dev/null and b/app/src/main/res/mipmap/icon_rounded.png differ diff --git a/app/src/main/res/mipmap/icon_rounded_512.png b/app/src/main/res/mipmap/icon_rounded_512.png new file mode 100644 index 0000000..6585742 Binary files /dev/null and b/app/src/main/res/mipmap/icon_rounded_512.png differ diff --git a/app/src/main/res/raw/chime_sound_7143.mp3 b/app/src/main/res/raw/chime_sound_7143.mp3 new file mode 100644 index 0000000..d9dbf34 Binary files /dev/null and b/app/src/main/res/raw/chime_sound_7143.mp3 differ diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..8f4f0ed --- /dev/null +++ b/app/src/main/res/values-es/strings.xml @@ -0,0 +1,40 @@ + + + Infinite Timer + v20250711 + com.hyperling.apps.infinitetimer + + + InfiniteTimer + DEBUG + ChronometerTime + LoopInterval + StartStop + AppOpen + ServiceRunning + + + Empezar + Terminar + Elige un Tiempo + Reiniciar + Pausa + Seguir + + 00:00:00 + + Horas + Minutos + Segundos + Millis + . + + Tiempo entre notificaciones: + Cuenta regresiva hasta notificación: + + Enable Millisecs + Enable Ads + Exit + + Volumen: + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8039e06..5df19ae 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ Infinite Timer - Test v201610291024 + v20250711 com.hyperling.apps.infinitetimer @@ -35,4 +35,6 @@ Enable Millisecs Enable Ads Exit + + Media Volume: diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4785832..c0cfd8f 100755 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - -