Compare commits

...

34 Commits

Author SHA1 Message Date
7e6c0b90e2 Merge pull request 'Volume Seekbar Fixes' (#3) from dev into main
Reviewed-on: #3
2025-08-17 08:49:15 -07:00
84c75f6be7 No need to call something in onCreate which is called in onResume. 2025-08-17 08:41:29 -07:00
1b3ca8c8af Only change the seekbar if it differs so that the volume sidebar does not show up on every open. 2025-08-17 08:40:56 -07:00
f83a8b41e4 Ensure volume bar is accurate if the app is minimized and the volume is modified before the app is reopened. 2025-08-17 08:35:13 -07:00
bc433b4185 Correctly add a space between "Volume" and the status. 2025-08-17 08:33:00 -07:00
34b13473c9 Show the article URL for proper accreditation. 2025-07-19 15:54:00 -07:00
a642b51532 Increment version for future development. 2025-07-19 15:52:02 -07:00
a5490b98c7 Hide release files (APKs and JSON) from Git. 2025-07-19 15:51:05 -07:00
140350012b Fix URL. 2025-07-19 15:50:40 -07:00
d610bfd40b Merge pull request 'v1.1.0 - Update Targeted Version + Enhancements + Bugfixes' (#1) from dev into main
# Enhancements
- Target Android 36 and update theme so that the app may be used properly on current and future versions of Android.
- Volume slider for easy viewing and changing of current settings.
- Add a default sound for devices lacking notification sounds.
- Add a rounded icon.

# Bugfixes
- App opening with Start already pressed.
- Large intervals (hundreds of hours) causing odd behavior.
2025-07-19 15:25:33 -07:00
b1c2dfaa6f Add a Spanish string resource. 2025-07-19 15:00:47 -07:00
e79ee82193 Add textview for volume control. 2025-07-19 15:00:26 -07:00
4f64ca49b7 Add a seek bar tied to media volume. 2025-07-13 19:31:15 -07:00
25969dd6d6 Fix the issue of a device not having any sounds to play by including a basic backup chime. 2025-07-13 18:38:17 -07:00
1fe3035050 Add a rounded icon and move icons to mipmap from drawable. 2025-07-13 18:37:09 -07:00
219ef6d64e Add more debug output regarding the prevention of large numbers. Reinstate missing assignment of longLoopInterval. 2025-07-13 12:47:27 -07:00
854f5df595 Set the versions to the newest SDK. Dock the minumum by 1 to match Carb Up. 2025-07-13 10:32:50 -07:00
dbb1af03c4 Finish fixing the large numbers bug by making 99:59:59 the maximum value allowed. Enhance the logging around resetting the StartStop key after a crash. 2025-07-13 10:32:19 -07:00
37e7df98af Update all debug log text. Fix bugs related to Start/Stop button. Start working on TODO for large value bug. 2025-07-12 15:43:47 -07:00
9bb729d5c2 Remove test from version name. 2025-07-12 15:31:07 -07:00
03380cf023 Putting things into place to fix a bug when the app is destroyed while the timer was running. 2025-07-12 13:18:52 -07:00
8da02333df Fix minute section displayin the hour's worth of minutes. 2025-07-12 12:39:11 -07:00
926fe17919 Remove unused imports. 2025-07-12 12:38:20 -07:00
fb007debad Change theme to DayNight rather than hardcoded to Light. Doesn't change much right now. 2025-07-12 12:38:08 -07:00
23772cedcb Remove the temporary app name header and add a margin for the true action bar. 2025-07-12 12:30:44 -07:00
e02abc19f8 Remove the temporary action bar hide. 2025-07-12 12:30:12 -07:00
fc6ff2e5cc Begin working on UI fixes. 2025-07-11 14:53:09 -07:00
4cd8641ec2 Update version. 2025-07-11 14:52:47 -07:00
15ff7fa1cf Clean up extra lines. 2025-07-11 14:52:37 -07:00
a3fe0d4a2e Delete deleteme's. 2025-07-11 14:30:40 -07:00
f055efed02 Project is now compiling and running on a test device successfully. 2025-07-11 14:30:15 -07:00
9446f427c3 Add link to a helpful upgrading article. 2025-07-11 13:38:53 -07:00
c57cdd0a35 Update gradle and android:gradle configurations so that the project builds successfully. 2025-07-11 13:38:27 -07:00
058e854606 Ensure app can be installed by future Android versions. 2025-07-11 13:13:04 -07:00
21 changed files with 441 additions and 143 deletions

2
.gitignore vendored
View File

@@ -33,3 +33,5 @@ google-services.json
# Android Profiling # Android Profiling
*.hprof *.hprof
# Release Files
app/release/*

View File

@@ -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. Timer which loops over the specified interval, playing your default notification sound each time it hits 0.
## Update Notes
### 2025-07-11
This article was very helpful in getting this app updated from SDK 25 to 36.
- https://sijus.medium.com/resurrecting-a-5-year-old-android-app-a-developers-journey-59d8f5689e5b
## Licenses
Chime sound byte came free from here:
https://pixabay.com/sound-effects/chime-sound-7143/

View File

@@ -1,14 +1,14 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 28 namespace "com.hyperling.apps.infinitetimer"
buildToolsVersion '28.0.3' compileSdkVersion 36
defaultConfig { defaultConfig {
applicationId "com.hyperling.apps.infinitetimer" applicationId "com.hyperling.apps.infinitetimer"
minSdkVersion 15 minSdkVersion 14
targetSdkVersion 28 targetSdkVersion 36
versionCode 7 versionCode 9
versionName "1.06" versionName "1.1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {

View File

@@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@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());
}
}

View File

@@ -1,18 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="com.hyperling.apps.infinitetimer">
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/icon_512" android:icon="@mipmap/icon_512"
android:roundIcon="@mipmap/icon_rounded_512"
android:label="@string/appName" android:label="@string/appName"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/InfiniteTimer" android:theme="@style/InfiniteTimer"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"> android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize">
<activity android:name=".MainActivity"> <activity android:name=".MainActivity" android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>

View File

@@ -1,28 +1,35 @@
package com.hyperling.apps.infinitetimer; package com.hyperling.apps.infinitetimer;
import android.app.NotificationManager;
import android.content.DialogInterface;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.Ringtone;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.ViewManager;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.TableRow; import android.widget.TableRow;
import android.widget.TextView; import android.widget.TextView;
import java.net.URI;
import java.util.Map; import java.util.Map;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
String TAG, String TAG = "MainActivity.",
keySharedPreferences, keyDebug, keyChronometerTime, keyLoopInterval, keySharedPreferences, keyDebug, keyChronometerTime, keyLoopInterval,
keyStartStop, keyAppOpen, keyServiceRunning, keyStartStop, keyAppOpen, keyServiceRunning,
stringChronometerDefault, stringChronometerTime, stringLoopInterval; stringChronometerDefault, stringChronometerTime, stringLoopInterval;
@@ -47,8 +54,15 @@ public class MainActivity extends AppCompatActivity {
Uri ringtoneUri; Uri ringtoneUri;
MediaPlayer mediaPlayer; MediaPlayer mediaPlayer;
SeekBar seekBar;
AudioManager audioManager;
TextView tvSeekBar;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
String tag = "onCreate";
if (DEBUG) Log.d(tag, "Starting");
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
@@ -56,7 +70,6 @@ public class MainActivity extends AppCompatActivity {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
/***** Get Strings *****/ /***** Get Strings *****/
TAG = getString(R.string.TAG);
keySharedPreferences = getString(R.string.keySharedPreferences); keySharedPreferences = getString(R.string.keySharedPreferences);
keyDebug = getString(R.string.keyDebug); keyDebug = getString(R.string.keyDebug);
keyChronometerTime = getString(R.string.keyChronometerTime); keyChronometerTime = getString(R.string.keyChronometerTime);
@@ -67,36 +80,83 @@ public class MainActivity extends AppCompatActivity {
stringChronometerDefault = getString(R.string.chronometerDefault); stringChronometerDefault = getString(R.string.chronometerDefault);
if (DEBUG) Log.d(tag, "Got strings.");
/***** Shared Preferences *****/ /***** Shared Preferences *****/
sharedPreferences = getSharedPreferences(keySharedPreferences, MODE_PRIVATE); sharedPreferences = getSharedPreferences(keySharedPreferences, MODE_PRIVATE);
// TODO: Comment these lines! DEBUG = sharedPreferences.getBoolean(keyDebug, true);
//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);
appOpen = sharedPreferences.getBoolean(keyAppOpen, false); appOpen = sharedPreferences.getBoolean(keyAppOpen, false);
serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false);
/* if (DEBUG) Log.d(tag, "Got preferences.");
if (!appOpen && !serviceRunning) {
sharedPreferences.edit().putBoolean(keyStartStop, false).apply();
}
*/
// Sound // Sound
ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
if (DEBUG) Log.d(tag, "ringtoneUri=" + ringtoneUri.toString());
mediaPlayer = MediaPlayer.create(this, ringtoneUri); 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){ if (DEBUG){
// Print all preferences // Print all preferences
Map<String, ?> sharedPreferencesAll = sharedPreferences.getAll(); Map<String, ?> sharedPreferencesAll = sharedPreferences.getAll();
for (String key : sharedPreferencesAll.keySet()){ 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,49 @@ public class MainActivity extends AppCompatActivity {
etMillis.setVisibility(View.GONE); etMillis.setVisibility(View.GONE);
//(ViewManager) etMillis.getParent().remove //(ViewManager) etMillis.getParent().remove
//sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); seekBar = findViewById(R.id.seekBar);
recoverScreen(); setVolumeControlStream(AudioManager.STREAM_MUSIC);
if (DEBUG) Log.d(TAG, "MainActivity.onCreate! Finished."); audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
seekBar.setMax(maxVolume);
tvSeekBar = findViewById(R.id.tvSeekbar);
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();
}
});
if (DEBUG) Log.d(tag, "Finished");
} }
private void doTimeChooser(){ private void doTimeChooser(){
if (DEBUG) Log.d(TAG, "MainActivity.openTimeChooser!"); String tag = TAG + "doTimeChooser";
if (DEBUG) Log.d(tag, "Starting");
//stringLoopInterval = "00:00:07"; //stringLoopInterval = "00:00:07";
//setEditTexts(stringLoopInterval); //setEditTexts(stringLoopInterval);
if (DEBUG) Log.d(tag, "Finished");
} }
private void setEditTexts(String interval){ 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)) { if (!interval.equals(stringChronometerDefault)) {
String[] time = interval.split(":"); String[] time = interval.split(":");
@@ -188,29 +276,67 @@ public class MainActivity extends AppCompatActivity {
} }
setLoopInterval(); setLoopInterval();
if (DEBUG) Log.d(tag, "Finished");
} }
private void setLoopInterval(){ 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 hours = 0, minutes = 0, seconds = 0;
int minutes = Integer.parseInt("0"+etMinutes.getText().toString()); try {
int seconds = Integer.parseInt("0"+etSeconds.getText().toString()); 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); 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); setChronometer(longLoopInterval);
if (DEBUG) Log.d(tag, "Finished");
} }
private void setChronometer(long milliseconds){ 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); //chronometer = (TextView) findViewById(R.id.chronometer);
String[] time = {Long.toString(milliseconds/(1000*60*60)), String[] time = {
Long.toString((milliseconds/(1000*60)%(60*60))), Long.toString(milliseconds/(1000*60*60)),
Long.toString((milliseconds/(1000))%60)}; Long.toString((milliseconds/(1000*60))%60),
Long.toString((milliseconds/(1000))%60)
};
for (int i = 0; i < time.length; i++){ for (int i = 0; i < time.length; i++){
while (time[i].length() < 2){ while (time[i].length() < 2){
@@ -229,16 +355,18 @@ public class MainActivity extends AppCompatActivity {
doTimeChooser(); doTimeChooser();
} }
});*/ });*/
if (DEBUG) Log.d(TAG, "MainActivity.setChronometer! display=" + display); if (DEBUG) Log.d(tag, "display=" + display);
sharedPreferences.edit().putString(keyChronometerTime, display).apply(); 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); 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){ private void startStopChronometer(String buttonText){
if (DEBUG) Log.d(TAG, "MainActivity.startStopChronometer!"); String tag = TAG + "startStopChronometer";
if (DEBUG) Log.d(tag, "Starting");
start = !start; start = !start;
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
@@ -274,10 +402,13 @@ public class MainActivity extends AppCompatActivity {
flipStartStopButton(); flipStartStopButton();
flipResetPauseButton(); flipResetPauseButton();
if (DEBUG) Log.d(tag, "Finished");
} }
private void recoverScreen(){ private void recoverScreen(){
if (DEBUG) Log.d(TAG, "MainActivity.recoverScreen!"); String tag = TAG + "recoverScreen";
if (DEBUG) Log.d(tag, "Starting");
/***** Retrieve the last settings *****/ /***** Retrieve the last settings *****/
// Chronometer // Chronometer
@@ -290,6 +421,7 @@ public class MainActivity extends AppCompatActivity {
// Start/Stop Button // Start/Stop Button
start = sharedPreferences.getBoolean(keyStartStop, false); start = sharedPreferences.getBoolean(keyStartStop, false);
if (DEBUG) Log.d(tag, "start=" + start);
flipStartStopButton(); flipStartStopButton();
flipResetPauseButton(); flipResetPauseButton();
@@ -301,10 +433,13 @@ public class MainActivity extends AppCompatActivity {
//startStopChronometer(stringChronometerTime); //startStopChronometer(stringChronometerTime);
//incrementTime.sendEmptyMessage(0); //incrementTime.sendEmptyMessage(0);
if (DEBUG) Log.d(tag, "Finished");
} }
private void flipStartStopButton(){ 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); start = sharedPreferences.getBoolean(keyStartStop, false);
serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false);
@@ -319,10 +454,13 @@ public class MainActivity extends AppCompatActivity {
} }
btnStartStop.setBackgroundColor(Color.GREEN); btnStartStop.setBackgroundColor(Color.GREEN);
} }
if (DEBUG) Log.d(tag, "Finished");
} }
private void resetPauseChronometer(){ 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); start = sharedPreferences.getBoolean(keyStartStop, false);
serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false);
@@ -333,10 +471,13 @@ public class MainActivity extends AppCompatActivity {
else{ else{
setEditTexts(stringChronometerDefault); setEditTexts(stringChronometerDefault);
} }
if (DEBUG) Log.d(tag, "Finished");
} }
private void flipResetPauseButton(){ 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); start = sharedPreferences.getBoolean(keyStartStop, false);
serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false); serviceRunning = sharedPreferences.getBoolean(keyServiceRunning, false);
@@ -349,13 +490,17 @@ public class MainActivity extends AppCompatActivity {
btnResetPause.setText(getString(R.string.btnReset)); btnResetPause.setText(getString(R.string.btnReset));
btnResetPause.setBackgroundColor(Color.RED); btnResetPause.setBackgroundColor(Color.RED);
} }
if (DEBUG) Log.d(tag, "Finished");
} }
private Handler incrementTime = new Handler(){ private final Handler incrementTime = new Handler(){
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
String tag = TAG + "handleMessage";
if (DEBUG) Log.d(tag, "Starting");
super.handleMessage(msg); super.handleMessage(msg);
if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage!");
appOpen = sharedPreferences.getBoolean(keyAppOpen, true); appOpen = sharedPreferences.getBoolean(keyAppOpen, true);
if (appOpen) { if (appOpen) {
@@ -372,7 +517,7 @@ public class MainActivity extends AppCompatActivity {
// Calculate new time and text // Calculate new time and text
setTimeLeft(); setTimeLeft();
if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage! longTimeLeft=" + longTimeLeft); if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft);
// Check if we need to beep // Check if we need to beep
if (longTimeLeft < 1000){ if (longTimeLeft < 1000){
@@ -381,12 +526,12 @@ public class MainActivity extends AppCompatActivity {
setStartTime(); setStartTime();
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longLoopInterval=" + longLoopInterval); Log.d(tag, "longLoopInterval=" + longLoopInterval);
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longStartTime=" + longStartTime); Log.d(tag, "longStartTime=" + longStartTime);
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longEndTime=" + longEndTime); Log.d(tag, "longEndTime=" + longEndTime);
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C longTimeLeft=" + longTimeLeft); Log.d(tag, "longTimeLeft=" + longTimeLeft);
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C waitInterval=" + waitInterval); Log.d(tag, "waitInterval=" + waitInterval);
Log.d(TAG, "MainActivity.incrementTime.handleMessage!C start=" + start); Log.d(tag, "start=" + start);
//chronometer.setText("Test"); //chronometer.setText("Test");
//setChronometer(200); //setChronometer(200);
/* /*
@@ -404,11 +549,11 @@ public class MainActivity extends AppCompatActivity {
synchronized (this){ synchronized (this){
try{ try{
wait(waitInterval); wait(waitInterval);
if (DEBUG) Log.d(TAG, "MainActivity.incrementTime.handleMessage! Done waiting."); if (DEBUG) Log.d(tag, "Done waiting.");
} }
catch (Exception e){ catch (Exception e){
e.printStackTrace(); 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(); //sharedPreferences.edit().putBoolean(keyStartStop, true).apply();
@@ -419,59 +564,148 @@ public class MainActivity extends AppCompatActivity {
Thread t = new Thread(task); Thread t = new Thread(task);
t.start(); t.start();
} }
if (DEBUG) Log.d(tag, "Finished");
} }
}; };
private void setStartTime(){ 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 (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 // Add extra 1000 so timer starts on interval and beeps after 1
longStartTime = System.currentTimeMillis() + 1000 - waitInterval; longStartTime = System.currentTimeMillis() + 1000 - waitInterval;
if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longStartTime=" + longStartTime); if (DEBUG) Log.d(tag, "longStartTime=" + longStartTime);
longEndTime = longStartTime + longLoopInterval; longEndTime = longStartTime + longLoopInterval;
if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longEndTime=" + longEndTime); if (DEBUG) Log.d(tag, "longEndTime=" + longEndTime);
} } else {
else{ if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft);
if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longTimeLeft==" + longTimeLeft);
longStartTime = System.currentTimeMillis() - waitInterval; longStartTime = System.currentTimeMillis() - waitInterval;
if (DEBUG) Log.d(TAG, "MainActivity.setStartTime! longStartTime=" + longStartTime); if (DEBUG) Log.d(tag, "longStartTime=" + longStartTime);
longEndTime = longStartTime + longTimeLeft; 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(){ 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; longTimeLeft = longEndTime - System.currentTimeMillis() + waitInterval;
if (DEBUG) Log.d(TAG, "MainActivity.setTimeLeft! longTimeLeft=" + longTimeLeft); if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft);
setChronometer(longTimeLeft); setChronometer(longTimeLeft);
if (DEBUG) Log.d(tag, "Finished");
} }
private void resetTimeLeft(){ private void resetTimeLeft(){
if (DEBUG) Log.d(TAG, "MainActivity.resetTimeLeft!"); String tag = TAG + "resetTimeLeft";
if (DEBUG) Log.d(tag, "Starting");
longTimeLeft = 0; longTimeLeft = 0;
setChronometer(longLoopInterval); setChronometer(longLoopInterval);
if (DEBUG) Log.d(tag, "Finished");
} }
private void playSound(){ 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) /* Original code, did not work on Nexus 7 with CM 12.1-20151117 (Android 5.1.1) * /
Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri); 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(); ringtone.play();
*/ /**/
//mediaPlayer.reset(); try {
mediaPlayer.start(); 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);
if (currVolume != seekBar.getProgress()) {
seekBar.setProgress(currVolume);
}
String volume = getString(R.string.tvSeekBar) + " " + seekBar.getProgress() + "/" + seekBar.getMax();
tvSeekBar.setText(volume);
if (DEBUG) Log.d(tag, "Finished");
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); String tag = TAG + "onPause";
if (DEBUG) Log.d(TAG, "MainActivity.onPause!"); if (DEBUG) Log.d(tag, "Starting");
stringLoopInterval = stringLoopInterval =
etHours.getText().toString() + ":" + etHours.getText().toString() + ":" +
@@ -483,22 +717,51 @@ public class MainActivity extends AppCompatActivity {
editor.putString(keyLoopInterval, stringLoopInterval); editor.putString(keyLoopInterval, stringLoopInterval);
editor.putBoolean(keyAppOpen, false); editor.putBoolean(keyAppOpen, false);
editor.apply(); editor.apply();
if (DEBUG) Log.d(tag, "Done with local code, calling super.");
super.onPause();
if (DEBUG) Log.d(tag, "Finished");
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.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(); sharedPreferences.edit().putBoolean(keyServiceRunning, false).apply();
recoverScreen(); recoverScreen();
syncVolume();
if (DEBUG) Log.d(tag, "Finished");
} }
private void flipInputTexts() { @Override
etHours.setEnabled(!start); protected void onDestroy() {
etMinutes.setEnabled(!start); String tag = TAG + "onDestroy";
etSeconds.setEnabled(!start); 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);
}}

View File

@@ -4,6 +4,7 @@
android:id="@+id/activity_main" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="?android:actionBarSize"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
@@ -157,4 +158,27 @@
</TableLayout> </TableLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tblButtons"
android:layout_alignParentBottom="true">
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/activity_vertical_margin"
android:layout_centerInParent="true"
android:indeterminate="false"/>
<TextView
android:id="@+id/tvSeekbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/seekBar"
android:layout_centerHorizontal="true"
android:text="@string/tvSeekBar"
android:textSize="14sp" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@@ -5,20 +5,18 @@
android:id="@+id/action_milis" android:id="@+id/action_milis"
android:orderInCategory="100" android:orderInCategory="100"
android:title="@string/action_millis" android:title="@string/action_millis"
app:showAsAction="never"
android:checkable="true" /> android:checkable="true" />
<item <item
android:id="@+id/action_enable_ads" android:id="@+id/action_enable_ads"
android:orderInCategory="150" android:orderInCategory="150"
android:title="@string/action_enable_ads" android:title="@string/action_enable_ads"
app:showAsAction="never"
android:checkable="true" /> android:checkable="true" />
<item <item
android:id="@+id/action_exit" android:id="@+id/action_exit"
android:orderInCategory="500" android:orderInCategory="500"
android:title="@string/action_exit" android:title="@string/action_exit"
app:showAsAction="never" /> />
</menu> </menu>

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

View File

@@ -0,0 +1,40 @@
<resources>
<!-- App details -->
<string name="appName">Infinite Timer</string>
<string name="appVersion">v20250711</string>
<string name="TAG">com.hyperling.apps.infinitetimer</string>
<!-- Keys -->
<string name="keySharedPreferences">InfiniteTimer</string>
<string name="keyDebug">DEBUG</string>
<string name="keyChronometerTime">ChronometerTime</string>
<string name="keyLoopInterval">LoopInterval</string>
<string name="keyStartStop">StartStop</string>
<string name="keyAppOpen">AppOpen</string>
<string name="keyServiceRunning">ServiceRunning</string>
<!-- UI Text -->
<string name="btnStart">Empezar</string>
<string name="btnStop">Terminar</string>
<string name="btnChooseTime">Elige un Tiempo</string>
<string name="btnReset">Reiniciar</string>
<string name="btnPause">Pausa</string>
<string name="btnResume">Seguir</string>
<string name="chronometerDefault">00:00:00</string>
<string name="tvHoursHint">Horas</string>
<string name="tvMinutesHint">Minutos</string>
<string name="tvSecondsHint">Segundos</string>
<string name="tvMillisHint">Millis</string>
<string name="tvDot">.</string>
<string name="tvUpperLabel"><b>Tiempo entre notificaciones:</b></string>
<string name="tvLowerLabel"><b>Cuenta regresiva hasta notificación:</b></string>
<string name="action_millis">Enable Millisecs</string>
<string name="action_enable_ads">Enable Ads</string>
<string name="action_exit">Exit</string>
<string name="tvSeekBar">Volumen: </string>
</resources>

View File

@@ -1,7 +1,7 @@
<resources> <resources>
<!-- App details --> <!-- App details -->
<string name="appName">Infinite Timer</string> <string name="appName">Infinite Timer</string>
<string name="appVersion">Test v201610291024</string> <string name="appVersion">v20250711</string>
<string name="TAG">com.hyperling.apps.infinitetimer</string> <string name="TAG">com.hyperling.apps.infinitetimer</string>
<!-- Keys --> <!-- Keys -->
@@ -35,4 +35,6 @@
<string name="action_millis">Enable Millisecs</string> <string name="action_millis">Enable Millisecs</string>
<string name="action_enable_ads">Enable Ads</string> <string name="action_enable_ads">Enable Ads</string>
<string name="action_exit">Exit</string> <string name="action_exit">Exit</string>
<string name="tvSeekBar">Media Volume:</string>
</resources> </resources>

View File

@@ -1,7 +1,7 @@
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
@@ -9,7 +9,7 @@
</style> </style>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="InfiniteTimer" parent="Theme.AppCompat.Light.DarkActionBar"> <style name="InfiniteTimer" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>

View File

@@ -1,17 +0,0 @@
package com.hyperling.apps.infinitetimer.deleteme;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View File

@@ -2,7 +2,10 @@
buildscript { buildscript {
repositories { repositories {
jcenter() mavenCentral()
maven {
url 'https://jitpack.io'
}
google() google()
maven { maven {
url 'https://maven.google.com/' url 'https://maven.google.com/'
@@ -10,7 +13,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:8.11.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@@ -19,7 +22,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
jcenter() mavenCentral()
google() google()
} }
} }

BIN
gimp/icon20250713.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,6 +1,6 @@
#Thu Jan 10 06:37:42 CST 2019 #Fri Jul 11 12:55:34 MST 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip