v1.1.0 - Update Targeted Version + Enhancements + Bugfixes #1

Merged
me merged 24 commits from dev into main 2025-07-19 15:25:35 -07:00
20 changed files with 440 additions and 142 deletions

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)[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/

View File

@@ -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 {

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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hyperling.apps.infinitetimer">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@drawable/icon_512"
android:icon="@mipmap/icon_512"
android:roundIcon="@mipmap/icon_rounded_512"
android:label="@string/appName"
android:supportsRtl="true"
android:theme="@style/InfiniteTimer"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize">
<activity android:name=".MainActivity">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View File

@@ -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<String, ?> 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);
}}

View File

@@ -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 @@
</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>

View File

@@ -5,20 +5,18 @@
android:id="@+id/action_milis"
android:orderInCategory="100"
android:title="@string/action_millis"
app:showAsAction="never"
android:checkable="true" />
<item
android:id="@+id/action_enable_ads"
android:orderInCategory="150"
android:title="@string/action_enable_ads"
app:showAsAction="never"
android:checkable="true" />
<item
android:id="@+id/action_exit"
android:orderInCategory="500"
android:title="@string/action_exit"
app:showAsAction="never" />
/>
</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>
<!-- App details -->
<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>
<!-- Keys -->
@@ -35,4 +35,6 @@
<string name="action_millis">Enable Millisecs</string>
<string name="action_enable_ads">Enable Ads</string>
<string name="action_exit">Exit</string>
<string name="tvSeekBar">Media Volume: </string>
</resources>

View File

@@ -1,7 +1,7 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
@@ -9,7 +9,7 @@
</style>
<!-- Base application theme. -->
<style name="InfiniteTimer" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="InfiniteTimer" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</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 {
repositories {
jcenter()
mavenCentral()
maven {
url 'https://jitpack.io'
}
google()
maven {
url 'https://maven.google.com/'
@@ -10,7 +13,7 @@ buildscript {
}
}
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
// in the individual module build.gradle files
@@ -19,7 +22,7 @@ buildscript {
allprojects {
repositories {
jcenter()
mavenCentral()
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
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip