diff --git a/.gitignore b/.gitignore index a8b0d1d..2d410d6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,44 @@ google-services.json # Android Profiling *.hprof +## Suggested ^^ + +## From TicTacToe project for good measure. vv +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +output.json + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files +*.jks +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof +/app/debug/output-metadata.json + +# Ha! +keystore/* +release diff --git a/app/.gitignore b/app/.gitignore new file mode 100755 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100755 index 0000000..34bb9ea --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + defaultConfig { + applicationId "com.hyperling.apps.the45minuterule" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 7 + versionName "1.06" + } + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + //compile fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + implementation group: 'com.android.support', name: 'appcompat-v7', version: '28.0.0' + //compile 'com.google.android.gms:play-services-ads:17.1.2' + implementation group: 'com.google.android.gms', name: 'play-services-ads', version: '17.1.2' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100755 index 0000000..675df9e --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/ling/WDBlue/Programming/Java/Android/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/hyperling/apps/the45minuterule/ApplicationTest.java b/app/src/androidTest/java/com/hyperling/apps/the45minuterule/ApplicationTest.java new file mode 100755 index 0000000..375ab29 --- /dev/null +++ b/app/src/androidTest/java/com/hyperling/apps/the45minuterule/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.hyperling.apps.the45minuterule; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100755 index 0000000..df39e66 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/hyperling/apps/the45minuterule/MainActivity.java b/app/src/main/java/com/hyperling/apps/the45minuterule/MainActivity.java new file mode 100755 index 0000000..a541f4c --- /dev/null +++ b/app/src/main/java/com/hyperling/apps/the45minuterule/MainActivity.java @@ -0,0 +1,483 @@ +package com.hyperling.apps.the45minuterule; + +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.AdView; + +import android.app.Dialog; +import android.app.TimePickerDialog; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.format.DateFormat; +import android.util.Log; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; +import android.widget.TimePicker; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Timer; +import java.util.TimerTask; + +public class MainActivity extends AppCompatActivity { + + // Primitive + boolean debug, alarmSet, adsEnabled; + int alarmHour, alarmMinute, currentHour, currentMinute; + final int TIME_PICKER_DIALOG = 0; + String TAG; + + // Java + Timer timer; + TimerTask timerTask; + + // Android + Resources recs; + + // Shared Preferences + private SharedPreferences sharedPreferences; + private String keySharedPreferences, keyAdsEnabled; + + // Views + Button btnTime; + RadioGroup radioGroup; + RadioButton rb1, rb2, rb3; + TextView digitalClockHeader; + TableLayout layClocks; + ArrayList clocks; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + //////////////////////////////////////////////////////////////////////////////////////////// + // Set up variables + recs = getResources(); + + debug = recs.getString(R.string.debug).toLowerCase().equals("true"); + + alarmSet = false; + + updateCurrentTime(); + + alarmHour = currentHour; + alarmMinute = currentMinute; + + // Strings + TAG = recs.getString(R.string.TAG); + keySharedPreferences = recs.getString(R.string.keySharedPreferences); + keyAdsEnabled = recs.getString(R.string.keyAdsEnabled); + + // Shared Preferences + sharedPreferences = getSharedPreferences(keySharedPreferences, MODE_PRIVATE); + + adsEnabled = sharedPreferences.getBoolean(keyAdsEnabled, false); + //////////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////////// + // Get the Views + btnTime = (Button) findViewById(R.id.btnTime); + + radioGroup = (RadioGroup) findViewById(R.id.rgTired); + + rb1 = (RadioButton) findViewById(R.id.rb1); + rb2 = (RadioButton) findViewById(R.id.rb2); + rb3 = (RadioButton) findViewById(R.id.rb3); + + digitalClockHeader = (TextView) findViewById(R.id.tvDigitalClocksHeader); + + layClocks = (TableLayout) findViewById(R.id.layClocks); + //////////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////////// + // Start manipulating stuff! + btnTime.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Check if the time is already being shown + showDialog(TIME_PICKER_DIALOG); + } + }); + + rb2.setChecked(true); + + radioGroup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (debug) System.out.println("Radio group was clicked"); + startCheckTime(false); + } + }); + + rb1.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (debug) System.out.println("Radio 1 was clicked"); + startCheckTime(false); + } + }); + + rb2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (debug) System.out.println("Radio 2 was clicked"); + startCheckTime(false); + } + }); + + rb3.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (debug) System.out.println("Radio 3 was clicked"); + startCheckTime(false); + } + }); + //////////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////////// + // Finally, load ads >:) + if ( adsEnabled) { + if (Build.VERSION.SDK_INT >= 9) { + // Load an ad into the AdMob banner view. + AdView adView = (AdView) findViewById(R.id.adView); + AdRequest adRequest = new AdRequest.Builder() + .addTestDevice(AdRequest.DEVICE_ID_EMULATOR) + .addTestDevice("C6A494DC6E7C9AC29102694D48487084") // Nexus 7 + .addTestDevice("B8B7561B850A9AB24E0D5B560FC50628") // Moto G + .addTestDevice("") // Cappy, even though it doesn't support ads + .setRequestAgent("android_studio:ad_template") + .build(); + adView.loadAd(adRequest); + } + } + //////////////////////////////////////////////////////////////////////////////////////////// + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + + MenuItem checkForNewArticles = menu.findItem(R.id.action_enable_ads); + checkForNewArticles.setChecked(adsEnabled); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + if (id == R.id.action_tips) { + startActivity(new Intent(MainActivity.this, Tips.class)); + return false; + } + + if (id == R.id.action_enable_ads) { + if (debug) Log.d(TAG, "MainActivity.onOptionsItemSelected: Before! adsEnabled=" + adsEnabled); + adsEnabled = !adsEnabled; + if (debug) Log.d(TAG, "MainActivity.onOptionsItemSelected: After! adsEnabled=" + adsEnabled); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(keyAdsEnabled, adsEnabled); + editor.commit(); + + if (adsEnabled){ + Toast.makeText(this, "Thank you!", Toast.LENGTH_LONG).show(); + } + else{ + Toast.makeText(this, "Disabled ads", Toast.LENGTH_LONG).show(); + } + + item.setChecked(adsEnabled); + + Intent intent = new Intent(MainActivity.this, MainActivity.class); + startActivity(intent); + finish(); + + return false; + } + + if (id == R.id.action_settings) { + Toast.makeText(this, "I can't do that, Jim", Toast.LENGTH_SHORT).show(); + return false; + } + + if (id == R.id.action_about) { + Toast.makeText(this, "I can't do that, Jim", Toast.LENGTH_SHORT).show(); + return false; + } + + if (id == R.id.action_help) { + Toast.makeText(this, "I can't do that, Jim", Toast.LENGTH_SHORT).show(); + return false; + } + + if (id == R.id.action_exit) { + finish(); + return false; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected Dialog onCreateDialog(int id){ + + switch(id){ + case TIME_PICKER_DIALOG: + if (debug) System.out.println("Showing time picker"); + + TimePickerDialog.OnTimeSetListener timeSetListener = new TimePickerDialog.OnTimeSetListener() { + @Override + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + if (debug) System.out.println("Time chosen is " + getTimeString(hourOfDay, minute)); + + alarmHour = hourOfDay; + alarmMinute = minute; + + btnTime.setText(getTimeString(alarmHour, alarmMinute)); + alarmSet = true; + startCheckTime(true); + } + }; + + TimePickerDialog timePickerDialog = new TimePickerDialog(this, timeSetListener, alarmHour, alarmMinute, DateFormat.is24HourFormat(getApplicationContext())); + + return timePickerDialog; + } + + if (debug) System.out.println("Returning null, id was " + id); + return null; + } + + private void calculateTimes(){ + if (debug) System.out.println("Calculating times"); + if (alarmHour == currentHour && alarmMinute == currentMinute){ + if (debug) System.out.println("Alarm time not set"); + return; + } + else{ + //////////////////////////////////////////////////////////////////////////////////////// + // Get how long we have until the alarm rings + int hourDifference, minuteDifference, minutesUntilAlarm, sleepiness; + + hourDifference = alarmHour; + minuteDifference = alarmMinute; + + if(alarmMinute < currentMinute){ + minuteDifference += 60; + hourDifference -= 1; + } + + if (alarmHour < currentHour){ + hourDifference += 24; + } + + hourDifference -= currentHour; + minuteDifference -= currentMinute; + + minutesUntilAlarm = (60*hourDifference) + minuteDifference; + if (debug) System.out.println("Number of minutes until the alarm rings: " + minutesUntilAlarm); + //////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////// + // Get sleepiness of the user + switch(radioGroup.getCheckedRadioButtonId()){ + case R.id.rb1: + sleepiness = Integer.parseInt(recs.getString(R.string.rb_opt1_value)); + break; + case R.id.rb2: + sleepiness = Integer.parseInt(recs.getString(R.string.rb_opt2_value)); + break; + case R.id.rb3: + sleepiness = Integer.parseInt(recs.getString(R.string.rb_opt3_value)); + break; + default: + sleepiness = 0; + } + if (debug) System.out.println("Sleepiness is " + sleepiness); + //////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////// + // Do math and set the clocks + int bedHour, bedMinute, remainder; + + bedHour = currentHour; + bedMinute = currentMinute; + + minutesUntilAlarm -= sleepiness; + + clocks = new ArrayList(); + if ((minutesUntilAlarm) > 0){ + remainder = (minutesUntilAlarm%45); + + if (remainder < 0){ + remainder += 45; + } + + bedMinute += remainder; + while (minutesUntilAlarm >= 45) { + + if (bedMinute >= 60) { + bedMinute -= 60; + bedHour += 1; + if (bedHour >= 24) { + bedHour -= 24; + } + } + + TextView newClock = new TextView(this); + newClock.setText(getTimeString(bedHour, bedMinute)); + + clocks.add(newClock); + + minutesUntilAlarm -= 45; + bedMinute += 45; + } + } + //////////////////////////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////////////////////////// + // Lay out the text views + TableRow.LayoutParams params = new TableRow.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1); + + int loop = 0; + TableRow newRow = new TableRow(this); + + layClocks.removeAllViews(); + + if (clocks.size() > 0) { + for (TextView clock : clocks) { + if (loop % 3 == 0) { + newRow = new TableRow(this); + layClocks.addView(newRow); + } + + params.column = (loop % 3) + 1; + + clock.setLayoutParams(params); + clock.setTextSize(18); + clock.setGravity(Gravity.CENTER); + + newRow.addView(clock); + + loop++; + } + + while (loop % 3 != 0){ + TextView blankClock = new TextView(this); + + params.column = (loop % 3) + 1; + + blankClock.setLayoutParams(params); + blankClock.setText(recs.getString(R.string.blank)); + blankClock.setTextSize(18); + blankClock.setGravity(Gravity.CENTER); + + newRow.addView(blankClock); + + loop++; + } + + // TODO: Remove temp string + String temp = recs.getString(R.string.main_bedtimes_header) + ". Refreshed at " + getTimeString(currentHour, currentMinute); + digitalClockHeader.setText(temp); + } + else{ + digitalClockHeader.setText(recs.getString(R.string.main_bedtimes_header_too_close)); + } + //////////////////////////////////////////////////////////////////////////////////////// + } + + return; + } + + public void startCheckTime(boolean setTimer){ + if (debug) System.out.println("Checking time"); + + updateCurrentTime(); + calculateTimes(); + + if (setTimer) { + timerTask = new TimerTask() { + @Override + public void run() { + startCheckTime(true); + } + }; + + timer = new Timer(); + + // TODO: Fix timer + //timer.schedule(timerTask, 5000); // Recalculate time every 5 seconds + } + + return; + } + + private void updateCurrentTime(){ + Calendar now = Calendar.getInstance(); + + currentHour = now.get(Calendar.HOUR_OF_DAY); + currentMinute = now.get(Calendar.MINUTE); + + currentMinute = now.get(Calendar.MINUTE); + currentHour = now.get(Calendar.HOUR_OF_DAY); + if (debug) System.out.println("Current time is " + getTimeString(currentHour, currentMinute)); + } + + private String getTimeString(int hour, int minute){ + String time, hours, minutes, meridian; + + if (minute < 10){ + minutes = "0" + Integer.toString(minute); + } + else{ + minutes = Integer.toString(minute); + } + + if (DateFormat.is24HourFormat(getApplicationContext())){ + hours = (hour < 10 ? "0"+Integer.toString(hour) : Integer.toString(hour)); + + time = hours + ":" + minutes; + } + else { + if (hour > 12){ + hour -= 12; + meridian = "PM"; + } + else{ + meridian = "AM"; + } + + hours = (hour == 0 ? "12" : Integer.toString(hour)); + + time = hours + ":" + minutes + meridian; + } + return time; + } +} diff --git a/app/src/main/java/com/hyperling/apps/the45minuterule/Tips.java b/app/src/main/java/com/hyperling/apps/the45minuterule/Tips.java new file mode 100755 index 0000000..ad6f011 --- /dev/null +++ b/app/src/main/java/com/hyperling/apps/the45minuterule/Tips.java @@ -0,0 +1,44 @@ +package com.hyperling.apps.the45minuterule; + +import android.content.res.Resources; +import android.graphics.Color; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class Tips extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tips); + + Resources recs = getResources(); + + String[] tips = recs.getStringArray(R.array.tips_tips_array); + + LinearLayout layout = (LinearLayout) findViewById(R.id.layTipsArray); + + int colorCounter = 0; + for (String tip : tips){ + TextView newTip = new TextView(this); + + newTip.setText(tip); + + newTip.setPadding(8,8,0,4); + + if (colorCounter % 2 == 0){ + newTip.setTextColor(recs.getColor(R.color.colorAccent)); + } + else{ + newTip.setTextColor(recs.getColor(R.color.colorPrimaryDark)); + } + + layout.addView(newTip); + + colorCounter++; + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100755 index 0000000..4b6fd0f --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + +