diff --git a/app/build.gradle b/app/build.gradle
index f948d5f..431ff8d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -30,4 +30,6 @@ dependencies {
apt 'com.google.auto.value:auto-value:1.2'
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'
+ compile 'com.android.support:support-v4:23.2.1'
+ compile 'com.android.support:recyclerview-v7:23.2.1'
}
diff --git a/app/src/androidTest/java/com/philliphsu/clock/ApplicationTest.java b/app/src/androidTest/java/com/philliphsu/clock2/ApplicationTest.java
similarity index 90%
rename from app/src/androidTest/java/com/philliphsu/clock/ApplicationTest.java
rename to app/src/androidTest/java/com/philliphsu/clock2/ApplicationTest.java
index 8bcdb38..1a61571 100644
--- a/app/src/androidTest/java/com/philliphsu/clock/ApplicationTest.java
+++ b/app/src/androidTest/java/com/philliphsu/clock2/ApplicationTest.java
@@ -1,4 +1,4 @@
-package com.philliphsu.clock;
+package com.philliphsu.clock2;
import android.app.Application;
import android.test.ApplicationTestCase;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0b00313..caae57a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
-
@@ -18,6 +18,18 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/philliphsu/clock/Alarm.java b/app/src/main/java/com/philliphsu/clock2/Alarm.java
similarity index 99%
rename from app/src/main/java/com/philliphsu/clock/Alarm.java
rename to app/src/main/java/com/philliphsu/clock2/Alarm.java
index 39b10f1..051902e 100644
--- a/app/src/main/java/com/philliphsu/clock/Alarm.java
+++ b/app/src/main/java/com/philliphsu/clock2/Alarm.java
@@ -1,4 +1,4 @@
-package com.philliphsu.clock;
+package com.philliphsu.clock2;
import com.google.auto.value.AutoValue;
diff --git a/app/src/main/java/com/philliphsu/clock/MainActivity.java b/app/src/main/java/com/philliphsu/clock2/MainActivity.java
similarity index 71%
rename from app/src/main/java/com/philliphsu/clock/MainActivity.java
rename to app/src/main/java/com/philliphsu/clock2/MainActivity.java
index 4a26458..bb01ce5 100644
--- a/app/src/main/java/com/philliphsu/clock/MainActivity.java
+++ b/app/src/main/java/com/philliphsu/clock2/MainActivity.java
@@ -1,24 +1,28 @@
-package com.philliphsu.clock;
+package com.philliphsu.clock2;
-import android.support.design.widget.TabLayout;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-
+import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
-import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-
import android.widget.TextView;
+import com.philliphsu.clock2.ringtone.RingtoneActivity;
+
public class MainActivity extends AppCompatActivity {
/**
@@ -58,8 +62,17 @@ public class MainActivity extends AppCompatActivity {
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
- .setAction("Action", null).show();
+ scheduleAlarm();
+ Snackbar.make(view, "Alarm set for 1 minute from now", Snackbar.LENGTH_LONG)
+ .setAction("Dismiss", new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
+ PendingIntent pi = alarmIntent();
+ am.cancel(pi);
+ pi.cancel();
+ }
+ }).show();
}
});
@@ -159,4 +172,25 @@ public class MainActivity extends AppCompatActivity {
return null;
}
}
+
+ private void scheduleAlarm() {
+ AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
+ // If there is already an alarm for this Intent scheduled (with the equality of two
+ // intents being defined by filterEquals(Intent)), then it will be removed and replaced
+ // by this one. For most of our uses, the relevant criteria for equality will be the
+ // action, the data, and the class (component). Although not documented, the request code
+ // of a PendingIntent is also considered to determine equality of two intents.
+ // todo: get alarm's ring time
+ am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 60000, alarmIntent());
+ }
+
+ private PendingIntent alarmIntent() {
+ // TODO: Use appropriate subclass instead
+ Intent intent = new Intent(this, RingtoneActivity.class)
+ .setData(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
+ // TODO: Use unique request codes per alarm.
+ // If a PendingIntent with this request code already exists, then we are likely modifying
+ // an alarm, so we should cancel the existing intent.
+ return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
}
diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java
new file mode 100644
index 0000000..52bf88b
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsAdapter.java
@@ -0,0 +1,77 @@
+package com.philliphsu.clock2.alarms;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.philliphsu.clock2.R;
+import com.philliphsu.clock2.alarms.dummy.DummyContent.DummyItem;
+
+import java.util.List;
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link DummyItem} and makes a call to the
+ * specified {@link AlarmsFragment.OnListFragmentInteractionListener}.
+ * TODO: Replace the implementation with code for your data type.
+ */
+public class AlarmsAdapter extends RecyclerView.Adapter {
+
+ private final List mValues;
+ private final AlarmsFragment.OnListFragmentInteractionListener mListener;
+
+ public AlarmsAdapter(List items, AlarmsFragment.OnListFragmentInteractionListener listener) {
+ mValues = items;
+ mListener = listener;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.fragment_alarms, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ holder.mItem = mValues.get(position);
+ holder.mIdView.setText(mValues.get(position).id);
+ holder.mContentView.setText(mValues.get(position).content);
+
+ holder.mView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (null != mListener) {
+ // Notify the active callbacks interface (the activity, if the
+ // fragment is attached to one) that an item has been selected.
+ mListener.onListFragmentInteraction(holder.mItem);
+ }
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mValues.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public final View mView;
+ public final TextView mIdView;
+ public final TextView mContentView;
+ public DummyItem mItem;
+
+ public ViewHolder(View view) {
+ super(view);
+ mView = view;
+ mIdView = (TextView) view.findViewById(R.id.id);
+ mContentView = (TextView) view.findViewById(R.id.content);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " '" + mContentView.getText() + "'";
+ }
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java
new file mode 100644
index 0000000..3252879
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/alarms/AlarmsFragment.java
@@ -0,0 +1,108 @@
+package com.philliphsu.clock2.alarms;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.philliphsu.clock2.R;
+import com.philliphsu.clock2.alarms.dummy.DummyContent;
+import com.philliphsu.clock2.alarms.dummy.DummyContent.DummyItem;
+
+/**
+ * A fragment representing a list of Items.
+ *
+ * Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener}
+ * interface.
+ */
+public class AlarmsFragment extends Fragment {
+
+ // TODO: Customize parameter argument names
+ private static final String ARG_COLUMN_COUNT = "column-count";
+ // TODO: Customize parameters
+ private int mColumnCount = 1;
+ private OnListFragmentInteractionListener mListener;
+
+ /**
+ * Mandatory empty constructor for the fragment manager to instantiate the
+ * fragment (e.g. upon screen orientation changes).
+ */
+ public AlarmsFragment() {
+ }
+
+ // TODO: Customize parameter initialization
+ @SuppressWarnings("unused")
+ public static AlarmsFragment newInstance(int columnCount) {
+ AlarmsFragment fragment = new AlarmsFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_COLUMN_COUNT, columnCount);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getArguments() != null) {
+ mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_alarms_list, container, false);
+
+ // Set the adapter
+ if (view instanceof RecyclerView) {
+ Context context = view.getContext();
+ RecyclerView recyclerView = (RecyclerView) view;
+ if (mColumnCount <= 1) {
+ recyclerView.setLayoutManager(new LinearLayoutManager(context));
+ } else {
+ recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
+ }
+ recyclerView.setAdapter(new AlarmsAdapter(DummyContent.ITEMS, mListener));
+ }
+ return view;
+ }
+
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (context instanceof OnListFragmentInteractionListener) {
+ mListener = (OnListFragmentInteractionListener) context;
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement OnListFragmentInteractionListener");
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mListener = null;
+ }
+
+ /**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ *
+ * See the Android Training lesson Communicating with Other Fragments for more information.
+ */
+ public interface OnListFragmentInteractionListener {
+ // TODO: Update argument type and name
+ void onListFragmentInteraction(DummyItem item);
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java b/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java
new file mode 100644
index 0000000..9a2dc3c
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/alarms/dummy/DummyContent.java
@@ -0,0 +1,72 @@
+package com.philliphsu.clock2.alarms.dummy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class for providing sample content for user interfaces created by
+ * Android template wizards.
+ *
+ * TODO: Replace all uses of this class before publishing your app.
+ */
+public class DummyContent {
+
+ /**
+ * An array of sample (dummy) items.
+ */
+ public static final List ITEMS = new ArrayList();
+
+ /**
+ * A map of sample (dummy) items, by ID.
+ */
+ public static final Map ITEM_MAP = new HashMap();
+
+ private static final int COUNT = 25;
+
+ static {
+ // Add some sample items.
+ for (int i = 1; i <= COUNT; i++) {
+ addItem(createDummyItem(i));
+ }
+ }
+
+ private static void addItem(DummyItem item) {
+ ITEMS.add(item);
+ ITEM_MAP.put(item.id, item);
+ }
+
+ private static DummyItem createDummyItem(int position) {
+ return new DummyItem(String.valueOf(position), "Item " + position, makeDetails(position));
+ }
+
+ private static String makeDetails(int position) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Details about Item: ").append(position);
+ for (int i = 0; i < position; i++) {
+ builder.append("\nMore details information here.");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * A dummy item representing a piece of content.
+ */
+ public static class DummyItem {
+ public final String id;
+ public final String content;
+ public final String details;
+
+ public DummyItem(String id, String content, String details) {
+ this.id = id;
+ this.content = content;
+ this.details = details;
+ }
+
+ @Override
+ public String toString() {
+ return content;
+ }
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java
new file mode 100644
index 0000000..7607dd5
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneActivity.java
@@ -0,0 +1,175 @@
+package com.philliphsu.clock2.ringtone;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.philliphsu.clock2.R;
+
+import static com.philliphsu.clock2.util.Preconditions.checkNotNull;
+
+/**
+ * An example full-screen activity that shows and hides the system UI (i.e.
+ * status bar and navigation/system bar) with user interaction.
+ *
+ * TODO: Make this abstract and make appropriate subclasses for Alarms and Timers.
+ * TODO: Use this together with RingtoneService.
+ */
+public class RingtoneActivity extends AppCompatActivity {
+ /**
+ * Whether or not the system UI should be auto-hidden after
+ * {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
+ */
+ private static final boolean AUTO_HIDE = true;
+
+ /**
+ * If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
+ * user interaction before hiding the system UI.
+ */
+ private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
+
+ /**
+ * Some older devices needs a small delay between UI widget updates
+ * and a change of the status and navigation bar.
+ */
+ private static final int UI_ANIMATION_DELAY = 300;
+ private final Handler mHideHandler = new Handler();
+ private View mContentView;
+ private final Runnable mHidePart2Runnable = new Runnable() {
+ @SuppressLint("InlinedApi")
+ @Override
+ public void run() {
+ // Delayed removal of status and navigation bar
+
+ // Note that some of these constants are new as of API 16 (Jelly Bean)
+ // and API 19 (KitKat). It is safe to use them, as they are inlined
+ // at compile-time and do nothing on earlier devices.
+ mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+ }
+ };
+ private View mControlsView;
+ private final Runnable mShowPart2Runnable = new Runnable() {
+ @Override
+ public void run() {
+ // Delayed display of UI elements
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.show();
+ }
+ mControlsView.setVisibility(View.VISIBLE);
+ }
+ };
+ private boolean mVisible;
+ private final Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hide();
+ }
+ };
+ /**
+ * Touch listener to use for in-layout UI controls to delay hiding the
+ * system UI. This is to prevent the jarring behavior of controls going away
+ * while interacting with activity UI.
+ */
+ private final View.OnTouchListener mDelayHideTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if (AUTO_HIDE) {
+ delayedHide(AUTO_HIDE_DELAY_MILLIS);
+ }
+ return false;
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_ringtone);
+ Uri ringtone = checkNotNull(getIntent().getData());
+ Intent intent = new Intent(this, RingtoneService.class).setData(ringtone);
+ startService(intent);
+
+ mVisible = true;
+ mControlsView = findViewById(R.id.fullscreen_content_controls);
+ mContentView = findViewById(R.id.fullscreen_content);
+
+
+ // Set up the user interaction to manually show or hide the system UI.
+ mContentView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ toggle();
+ }
+ });
+
+ // Upon interacting with UI controls, delay any scheduled hide()
+ // operations to prevent the jarring behavior of controls going away
+ // while interacting with the UI.
+ findViewById(R.id.dummy_button).setOnTouchListener(mDelayHideTouchListener);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+
+ // Trigger the initial hide() shortly after the activity has been
+ // created, to briefly hint to the user that UI controls
+ // are available.
+ delayedHide(100);
+ }
+
+ private void toggle() {
+ if (mVisible) {
+ hide();
+ } else {
+ show();
+ }
+ }
+
+ private void hide() {
+ // Hide UI first
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.hide();
+ }
+ mControlsView.setVisibility(View.GONE);
+ mVisible = false;
+
+ // Schedule a runnable to remove the status and navigation bar after a delay
+ mHideHandler.removeCallbacks(mShowPart2Runnable);
+ mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
+ }
+
+ @SuppressLint("InlinedApi")
+ private void show() {
+ // Show the system bar
+ mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ mVisible = true;
+
+ // Schedule a runnable to display UI elements after a delay
+ mHideHandler.removeCallbacks(mHidePart2Runnable);
+ mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
+ }
+
+ /**
+ * Schedules a call to hide() in [delay] milliseconds, canceling any
+ * previously scheduled calls.
+ */
+ private void delayedHide(int delayMillis) {
+ mHideHandler.removeCallbacks(mHideRunnable);
+ mHideHandler.postDelayed(mHideRunnable, delayMillis);
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java
new file mode 100644
index 0000000..2424438
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/ringtone/RingtoneService.java
@@ -0,0 +1,119 @@
+package com.philliphsu.clock2.ringtone;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+import com.philliphsu.clock2.R;
+
+import static com.philliphsu.clock2.util.Preconditions.checkNotNull;
+
+/**
+ * Runs in the foreground. While it can still be killed by the system, it stays alive significantly
+ * longer than if it does not run in the foreground. The longevity should be sufficient for practical
+ * use. In fact, if the app is used properly, longevity should be a non-issue; realistically, the lifetime
+ * of the RingtoneService will be tied to that of its RingtoneActivity because users are not likely to
+ * navigate away from the Activity without making an action. But if they do accidentally navigate away,
+ * they have plenty of time to make the desired action via the notification.
+ */
+public class RingtoneService extends Service {
+ private static final String TAG = "RingtoneService";
+
+ private AudioManager mAudioManager;
+ private Ringtone mRingtone;
+ private boolean mAutoSilenced = false;
+ private final Handler mSilenceHandler = new Handler();
+ private final Runnable mSilenceRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mAutoSilenced = true;
+ stopSelf();
+ }
+ };
+
+ // TODO: Apply the setting for "Silence after" here by using an AlarmManager to
+ // schedule an alarm in the future to stop this service, and also update the foreground
+ // notification to say "alarm missed" in the case of Alarms or "timer expired" for Timers.
+ // If Alarms and Timers will have distinct settings for this, then consider doing this
+ // operation in the respective subclass of this service.
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (mAudioManager == null && mRingtone == null) {
+ Uri ringtone = checkNotNull(intent.getData());
+ // TODO: The below call requires a notification, and there is no way to provide one suitable
+ // for both Alarms and Timers. Consider making this class abstract, and have subclasses
+ // implement an abstract method that calls startForeground(). You would then call that
+ // method here instead.
+ Notification note = new NotificationCompat.Builder(this)
+ // Required contents
+ .setSmallIcon(R.mipmap.ic_launcher) // TODO: alarm icon
+ .setContentTitle("Foreground RingtoneService")
+ .setContentText("Ringtone is playing in the foreground.")
+ .build();
+ startForeground(R.id.ringtone_service_notification, note); // TOneverDO: Pass 0 as the first argument
+
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ // Request audio focus first, so we don't play our ringtone on top of any
+ // other apps that currently have playback.
+ int result = mAudioManager.requestAudioFocus(
+ null, // Playback will likely be short, so don't worry about listening for focus changes
+ AudioManager.STREAM_ALARM,
+ // Request permanent focus, as ringing could last several minutes
+ AudioManager.AUDIOFOCUS_GAIN);
+ if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ mRingtone = RingtoneManager.getRingtone(this, ringtone);
+ // Deprecated, but the alternative AudioAttributes requires API 21
+ mRingtone.setStreamType(AudioManager.STREAM_ALARM);
+ mRingtone.play();
+ scheduleAutoSilence();
+ }
+ }
+ // If killed while started, don't recreate
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy()");
+ mRingtone.stop();
+ mAudioManager.abandonAudioFocus(null); // no listener was set
+ mSilenceHandler.removeCallbacks(mSilenceRunnable);
+ if (mAutoSilenced) {
+ // Post notification that alarm was missed, or timer expired.
+ // TODO: You should probably do this in the appropriate subclass.
+ NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ Notification note = new NotificationCompat.Builder(this)
+ .setContentTitle("Missed alarm")
+ .setContentText("Regular alarm time here")
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .build();
+ nm.notify("tag", 0, note);
+ }
+ stopForeground(true);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null; // Binding to this service is not supported
+ }
+
+ private void scheduleAutoSilence() {
+ // TODO: Read prefs
+ //SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
+ int minutes = 2; /*Integer.parseInt(pref.getString(
+ getString(R.string.key_silence_after),
+ "15"));*/
+ mSilenceHandler.postDelayed(mSilenceRunnable, minutes * 60000);
+ }
+}
diff --git a/app/src/main/java/com/philliphsu/clock2/util/Preconditions.java b/app/src/main/java/com/philliphsu/clock2/util/Preconditions.java
new file mode 100644
index 0000000..7dedac8
--- /dev/null
+++ b/app/src/main/java/com/philliphsu/clock2/util/Preconditions.java
@@ -0,0 +1,14 @@
+package com.philliphsu.clock2.util;
+
+/**
+ * Created by Phillip Hsu on 5/28/2016.
+ */
+public final class Preconditions {
+ private Preconditions() {}
+
+ public static T checkNotNull(T obj) {
+ if (null == obj)
+ throw new NullPointerException();
+ return obj;
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index ce3a239..0b90552 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,42 +1,28 @@
-
+
-
-
-
-
-
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay">
-
+
+ android:layout_height="match_parent"/>
-
+
diff --git a/app/src/main/res/layout/activity_ringtone.xml b/app/src/main/res/layout/activity_ringtone.xml
new file mode 100644
index 0000000..f63dac5
--- /dev/null
+++ b/app/src/main/res/layout/activity_ringtone.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_alarms.xml b/app/src/main/res/layout/fragment_alarms.xml
new file mode 100644
index 0000000..39fe45d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_alarms.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_alarms_list.xml b/app/src/main/res/layout/fragment_alarms_list.xml
new file mode 100644
index 0000000..7e0dfec
--- /dev/null
+++ b/app/src/main/res/layout/fragment_alarms_list.xml
@@ -0,0 +1,14 @@
+
+
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index 3099ffc..dd914e3 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -6,7 +6,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.philliphsu.clock.MainActivity$PlaceholderFragment">
+ tools:context="com.philliphsu.clock2.MainActivity$PlaceholderFragment">
+ tools:context="com.philliphsu.clock2.MainActivity">
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9c..227fd33 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,4 +3,6 @@
#3F51B5#303F9F#FF4081
+
+ #66000000
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index cef3abc..7d9415a 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,4 +4,5 @@
16dp16dp8dp
+ 16dp
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 0000000..9d8efd4
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a5b61dc..4956c0e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,9 @@
- Clock+
+ Clock+2SettingsHello World from section: %1$d
+
+ Clock+
+ Dummy Button
+ DUMMY\nCONTENT
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 177cefc..71d13c4 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -17,4 +17,16 @@
+
+
+
+
diff --git a/app/src/test/java/com/philliphsu/clock/AlarmTest.java b/app/src/test/java/com/philliphsu/clock2/AlarmTest.java
similarity index 99%
rename from app/src/test/java/com/philliphsu/clock/AlarmTest.java
rename to app/src/test/java/com/philliphsu/clock2/AlarmTest.java
index 187632e..40d7981 100644
--- a/app/src/test/java/com/philliphsu/clock/AlarmTest.java
+++ b/app/src/test/java/com/philliphsu/clock2/AlarmTest.java
@@ -1,4 +1,4 @@
-package com.philliphsu.clock;
+package com.philliphsu.clock2;
import org.junit.Test;
diff --git a/app/src/test/java/com/philliphsu/clock/ExampleUnitTest.java b/app/src/test/java/com/philliphsu/clock2/ExampleUnitTest.java
similarity index 89%
rename from app/src/test/java/com/philliphsu/clock/ExampleUnitTest.java
rename to app/src/test/java/com/philliphsu/clock2/ExampleUnitTest.java
index 5b7d987..3a42ca2 100644
--- a/app/src/test/java/com/philliphsu/clock/ExampleUnitTest.java
+++ b/app/src/test/java/com/philliphsu/clock2/ExampleUnitTest.java
@@ -1,4 +1,4 @@
-package com.philliphsu.clock;
+package com.philliphsu.clock2;
import org.junit.Test;