EmergencyInfo V1 App code
Bug: 23591361
Change-Id: I4a973ad0a50cc4a39c2e4c84bd7a8f772cc3f359
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..2125da4
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(TARGET_BUILD_APPS),)
+support_library_root_dir := frameworks/support
+else
+support_library_root_dir := prebuilts/sdk/current/support
+endif
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v14-preference \
+ android-support-v13 \
+ android-support-v7-appcompat \
+ android-support-v7-preference \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-design
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res \
+ $(support_library_root_dir)/v7/appcompat/res \
+ $(support_library_root_dir)/v7/preference/res \
+ $(support_library_root_dir)/v7/recyclerview/res \
+ $(support_library_root_dir)/v14/preference/res \
+ $(support_library_root_dir)/design/res
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+ --extra-packages android.support.v7.preference \
+ --extra-packages android.support.v14.preference \
+ --extra-packages android.support.v7.appcompat \
+ --extra-packages android.support.v7.recyclerview \
+ --extra-packages android.support.design
+
+LOCAL_SDK_VERSION := current
+LOCAL_PACKAGE_NAME := EmergencyInfo
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..0bbacad
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.emergency">
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <application
+ android:label="@string/app_name"
+ android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+ <activity
+ android:name=".ViewInfoActivity"
+ android:showOnLockScreen="true">
+ <intent-filter>
+ <action android:name="android.telephony.action.EMERGENCY_ASSISTANCE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".EditInfoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/res/drawable/ic_info_black_24dp.xml b/res/drawable/ic_info_black_24dp.xml
new file mode 100644
index 0000000..34b8202
--- /dev/null
+++ b/res/drawable/ic_info_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" />
+</vector>
diff --git a/res/drawable/ic_notifications_black_24dp.xml b/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 0000000..e3400cf
--- /dev/null
+++ b/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" />
+</vector>
diff --git a/res/drawable/ic_sync_black_24dp.xml b/res/drawable/ic_sync_black_24dp.xml
new file mode 100644
index 0000000..3f0ac1c
--- /dev/null
+++ b/res/drawable/ic_sync_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" />
+</vector>
\ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher.png b/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher.png b/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher.png b/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher.png b/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher.png b/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
new file mode 100644
index 0000000..dbbdd40
--- /dev/null
+++ b/res/values-v21/styles.xml
@@ -0,0 +1,9 @@
+<resources>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
new file mode 100644
index 0000000..812cb7b
--- /dev/null
+++ b/res/values/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..9e16bf8
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,33 @@
+<resources>
+ <string name="app_name">Emergency Information</string>
+
+ <string name="medical_category">Health</string>
+ <string name="blood_type">Blood Type</string>
+ <string-array name="blood_type_values">
+ <item>O+</item>
+ <item>O-</item>
+ <item>A+</item>
+ <item>A-</item>
+ <item>B+</item>
+ <item>B-</item>
+ <item>AB+</item>
+ <item>AB-</item>
+ </string-array>
+
+ <string name="allergies">Allergies</string>
+ <string name="default_summary">None specified</string>
+
+ <string name="medications">Medications</string>
+ <string name="medical_conditions">Medical conditions and notes</string>
+
+ <string name="readonly_title">Lockscreen view</string>
+
+ <string name="add_emergency_contact">Add contact</string>
+ <string-array name="contact_action_choices">
+ <item>Delete emergency contact</item>
+ <item>View details</item>
+ </string-array>
+
+ <string name="unknown_contact">Unknown Contact</string>
+
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..b2fb37b
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,15 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
diff --git a/res/xml/emergency_info.xml b/res/xml/emergency_info.xml
new file mode 100644
index 0000000..7d94466
--- /dev/null
+++ b/res/xml/emergency_info.xml
@@ -0,0 +1,59 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
+ dismiss it. -->
+ <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
+ <ListPreference
+ android:defaultValue="-1"
+ android:entries="@array/blood_type_values"
+ android:entryValues="@array/blood_type_values"
+ android:key="blood_type"
+ android:negativeButtonText="@null"
+ android:positiveButtonText="@null"
+ android:summary="@string/default_summary"
+ android:title="@string/blood_type" />
+
+ <!-- NOTE: EditTextPreference accepts EditText attributes. -->
+ <!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
+ <EditTextPreference
+ android:capitalize="sentences"
+ android:inputType="textCapWords"
+ android:key="allergies"
+ android:maxLines="1"
+ android:selectAllOnFocus="true"
+ android:singleLine="true"
+ android:summary="@string/default_summary"
+ android:title="@string/allergies" />
+
+ <EditTextPreference
+ android:capitalize="sentences"
+ android:inputType="textCapWords"
+ android:key="medications"
+ android:maxLines="1"
+ android:selectAllOnFocus="true"
+ android:singleLine="true"
+ android:summary="@string/default_summary"
+ android:title="@string/medications" />
+
+ <EditTextPreference
+ android:capitalize="sentences"
+ android:inputType="textCapWords"
+ android:key="medical_conditions"
+ android:maxLines="1"
+ android:selectAllOnFocus="true"
+ android:singleLine="false"
+ android:title="@string/medical_conditions" />
+ <PreferenceCategory android:key="emergency_contacts" android:title="Emergency contacts">
+ </PreferenceCategory>
+
+ <PreferenceCategory android:key="debug" android:title="Debug">
+ <Preference
+ android:title="@string/readonly_title"
+ android:key="readonly"
+ >
+ <intent android:action="android.telephony.action.EMERGENCY_ASSISTANCE" />
+ </Preference>
+ </PreferenceCategory>
+
+
+</PreferenceScreen>
diff --git a/src/com/android/emergency/AppCompatPreferenceActivity.java b/src/com/android/emergency/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..92c7843
--- /dev/null
+++ b/src/com/android/emergency/AppCompatPreferenceActivity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatDelegate;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A {@link PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ /**
+ * Sets up the {@link ActionBar}, if the API is available.
+ */
+ protected void setupActionBar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ // Show the Up button in the action bar.
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ // Respond to the action bar's Up/Home button.
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/src/com/android/emergency/ContactActionsDialogFragment.java b/src/com/android/emergency/ContactActionsDialogFragment.java
new file mode 100644
index 0000000..673d83f
--- /dev/null
+++ b/src/com/android/emergency/ContactActionsDialogFragment.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+/**
+ * A {@link DialogFragment} that displays a list of actions for an emergency contact. Call
+ * {@link #setTitle(CharSequence)} and {@link #setDialogActionCallback(DialogActionCallback)}
+ * before showing.
+ */
+public class ContactActionsDialogFragment extends DialogFragment {
+ private CharSequence mTitle = null;
+ private DialogActionCallback mDialogActionCallback = null;
+
+ /**
+ * Sets the title of the dialog.
+ */
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ }
+
+ /**
+ * Sets the callbacks to be triggered when dialog options are selected.
+ */
+ public void setDialogActionCallback(DialogActionCallback dialogActionCallback) {
+ mDialogActionCallback = dialogActionCallback;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ if (mTitle == null) {
+ throw new IllegalArgumentException(
+ "setTitle must be called before showing dialog");
+ }
+ if (mDialogActionCallback == null) {
+ throw new IllegalArgumentException(
+ "setDialogActionCallback must be called before showing dialog");
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ String[] choices =
+ getContext().getResources().getStringArray(R.array.contact_action_choices);
+
+ builder.setTitle(mTitle).setItems(choices, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == 0) {
+ mDialogActionCallback.onContactDelete();
+ } else if (which == 1) {
+ mDialogActionCallback.onContactDisplay();
+ }
+ }
+ });
+ return builder.create();
+ }
+
+ /**
+ * Callbacks for actions on a contact. Triggered when options in the dialog are selected.
+ */
+ public interface DialogActionCallback {
+ /**
+ * Callback to delete a contact.
+ */
+ void onContactDelete();
+
+ /**
+ * Callback to display a contact.
+ */
+ void onContactDisplay();
+ }
+}
diff --git a/src/com/android/emergency/ContactPreference.java b/src/com/android/emergency/ContactPreference.java
new file mode 100644
index 0000000..3ea8d39
--- /dev/null
+++ b/src/com/android/emergency/ContactPreference.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.preference.Preference;
+import android.provider.ContactsContract;
+
+/**
+ * A {@link Preference} to display a contact using the specified URI string.
+ */
+public class ContactPreference extends Preference {
+
+ private final Uri mUri;
+
+ /**
+ * Instantiates a ContactPreference that displays an emergency contact, taking in a Context and
+ * the Uri of the contact as a String.
+ */
+ public ContactPreference(Context context, String uriString) {
+ super(context);
+ mUri = Uri.parse(uriString);
+ String name = getName();
+ setTitle((name != null) ? name : getContext().getString(R.string.unknown_contact));
+ }
+
+ /**
+ * Displays a contact card for the contact.
+ */
+ public void displayContact() {
+ Intent contactIntent = new Intent(Intent.ACTION_VIEW);
+ contactIntent.setData(getUri());
+ getContext().startActivity(contactIntent);
+ }
+
+ /**
+ * Returns the URI for the contact.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ private String getName() {
+ Cursor cursor = getContext().getContentResolver().query(getUri(), null, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(cursor.getColumnIndex(
+ ContactsContract.Contacts.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/emergency/EditInfoActivity.java b/src/com/android/emergency/EditInfoActivity.java
new file mode 100644
index 0000000..1da4089
--- /dev/null
+++ b/src/com/android/emergency/EditInfoActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.os.Bundle;
+
+/**
+ * Activity for editing emergency information.
+ */
+public class EditInfoActivity extends AppCompatPreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setupActionBar();
+
+ // Create the fragment with readOnly set to false
+ EmergencyInfoFragment emergencyInfoFragment = EmergencyInfoFragment
+ .createEmergencyInfoFragment(false);
+
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction().replace(android.R.id.content,
+ emergencyInfoFragment).commit();
+ }
+}
diff --git a/src/com/android/emergency/EmergencyInfoFragment.java b/src/com/android/emergency/EmergencyInfoFragment.java
new file mode 100644
index 0000000..666c65d
--- /dev/null
+++ b/src/com/android/emergency/EmergencyInfoFragment.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import android.preference.PreferenceScreen;
+import android.provider.ContactsContract;
+import android.support.v4.content.ContextCompat;
+import android.Manifest;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Fragment that displays health information and emergency contacts.
+ * Takes in boolean readOnly to determine whether or not to allow information to be edited.
+ */
+public class EmergencyInfoFragment extends PreferenceFragment {
+
+ /** Result code for contact picker */
+ private static final int CONTACT_PICKER_RESULT = 1001;
+
+ /** Request code for runtime contacts permission */
+ private static final int CONTACT_PERMISSION_REQUEST = 1002;
+
+ /** Key for contact actions dialog */
+ private static final String CONTACT_ACTIONS_DIALOG_KEY = "contact_actions";
+
+ /** Key for debug preference */
+ private static final String DEBUG_KEY = "debug";
+
+ /** Key for emergency contacts preference */
+ private static final String EMERGENCY_CONTACTS_KEY = "emergency_contacts";
+
+ /** Key to look up whether or not the fragment should be read only from the bundle */
+ private static final String READ_ONLY_KEY = "read_only";
+
+ /** Keys for all editable preferences- used to set up bindings */
+ private static final String[] PREFERENCE_KEYS = {"blood_type", "allergies", "medications",
+ "medical_conditions"};
+
+ /** Whether or not this fragment should be read only */
+ private boolean mReadOnly;
+
+ /** SharedPreferences- initialized in onCreate */
+ private SharedPreferences mSharedPreferences = null;
+
+ /** Reference to the preferenceScreen controlled by this fragment */
+ private PreferenceScreen mPreferenceScreen;
+
+ /**
+ * Creates a new EmergencyInfoFragment that can be used to edit user info if {@code readOnly}
+ * is false. Otherwise, it provides a non-editable view of the emergency info.
+ */
+ public static EmergencyInfoFragment createEmergencyInfoFragment(boolean readOnly) {
+ Bundle emergencyInfoArgs = new Bundle();
+ emergencyInfoArgs.putBoolean(READ_ONLY_KEY, readOnly);
+ EmergencyInfoFragment emergencyInfoFragment = new EmergencyInfoFragment();
+ emergencyInfoFragment.setArguments(emergencyInfoArgs);
+ return emergencyInfoFragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.emergency_info);
+
+ mReadOnly = getArguments().getBoolean(READ_ONLY_KEY);
+ mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+ mPreferenceScreen = getPreferenceScreen();
+
+ for (String preferenceKey : PREFERENCE_KEYS) {
+ Preference preference = findPreference(preferenceKey);
+ bindPreferenceSummaryToValue(preference);
+ if (mReadOnly) {
+ preference.setEnabled(false);
+ preference.setShouldDisableView(false);
+ }
+ }
+ populateEmergencyContacts();
+ if (mReadOnly) {
+ // TODO: For supporting testing only. Remove this for launch.
+ PreferenceCategory debugPreferenceCategory =
+ (PreferenceCategory) findPreference(DEBUG_KEY);
+ mPreferenceScreen.removePreference(debugPreferenceCategory);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == CONTACT_PICKER_RESULT && resultCode == Activity.RESULT_OK) {
+ Uri result = data.getData();
+ // Manipulate a copy of emergency contacts rather than editing directly- see
+ // getEmergencyContacts for why this is necessary.
+ Set<String> oldContacts = getEmergencyContacts();
+ ArraySet<String> newContacts = new ArraySet<String>(oldContacts.size() + 1);
+ newContacts.addAll(oldContacts);
+
+ newContacts.add(result.toString());
+ setEmergencyContacts(newContacts);
+
+ populateEmergencyContacts();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ if (requestCode == CONTACT_PERMISSION_REQUEST) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ populateEmergencyContacts();
+ } else {
+ mPreferenceScreen.removePreference(findPreference(EMERGENCY_CONTACTS_KEY));
+ }
+ }
+ }
+
+ private static final Preference.OnPreferenceChangeListener
+ sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ String stringValue = value.toString();
+
+ if (preference instanceof ListPreference) {
+ ListPreference listPreference = (ListPreference) preference;
+ int index = listPreference.findIndexOfValue(stringValue);
+ preference.setSummary(index >= 0 ? listPreference.getEntries()[index] : null);
+ } else {
+ preference.setSummary(stringValue);
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Binds a preference's summary to its value. More specifically, when the
+ * preference's value is changed, its summary (line of text below the
+ * preference title) is updated to reflect the value. The summary is also
+ * immediately updated upon calling this method. The exact display format is
+ * dependent on the type of preference.
+ *
+ * @see #sBindPreferenceSummaryToValueListener
+ */
+ private void bindPreferenceSummaryToValue(Preference preference) {
+ preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
+ mSharedPreferences.getString(preference.getKey(), ""));
+ }
+
+ private void populateEmergencyContacts() {
+ PreferenceCategory emergencyContactsCategory =
+ (PreferenceCategory) findPreference(EMERGENCY_CONTACTS_KEY);
+ emergencyContactsCategory.removeAll();
+ Set<String> emergencyContacts = getEmergencyContacts();
+
+ if (!emergencyContacts.isEmpty()) {
+ // Get permission if necessary, else populate emergency contacts list
+ if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+ CONTACT_PERMISSION_REQUEST);
+ } else {
+ for (String contactUri : emergencyContacts) {
+ final ContactPreference contactPreference =
+ new ContactPreference(getContext(), contactUri);
+ contactPreference.setOnPreferenceClickListener(
+ createContactPreferenceClickListener(contactPreference));
+ emergencyContactsCategory.addPreference(contactPreference);
+ }
+ }
+ }
+
+ if (!mReadOnly) {
+ // If in edit mode, add a button to create a new emergency contact.
+ emergencyContactsCategory.addPreference(createAddEmergencyContactPreference());
+ } else if (emergencyContacts.isEmpty()) {
+ // If in view mode and there are no contacts, remove the section entirely.
+ mPreferenceScreen.removePreference(emergencyContactsCategory);
+ }
+ }
+
+ private Preference.OnPreferenceClickListener createContactPreferenceClickListener(
+ final ContactPreference contactPreference) {
+ return new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ final Uri contactUri = contactPreference.getUri();
+
+ if (mReadOnly) {
+ // TODO: Call the contact instead of displaying a card.
+ contactPreference.displayContact();
+ } else {
+ ContactActionsDialogFragment contactActionsDialogFragment =
+ new ContactActionsDialogFragment();
+ contactActionsDialogFragment.setTitle(contactPreference.getTitle());
+ contactActionsDialogFragment.setDialogActionCallback(
+ new ContactActionsDialogFragment.DialogActionCallback() {
+ @Override
+ public void onContactDelete() {
+ // Manipulate a copy of emergency contacts rather than
+ // editing directly- see getEmergencyContacts for why
+ // this is necessary.
+ Set<String> oldContacts = getEmergencyContacts();
+ ArraySet<String> newContacts = new ArraySet<String>(
+ oldContacts.size());
+
+ newContacts.addAll(oldContacts);
+ newContacts.remove(contactUri.toString());
+ setEmergencyContacts(newContacts);
+
+ populateEmergencyContacts();
+ }
+
+ @Override
+ public void onContactDisplay() {
+ contactPreference.displayContact();
+ }
+ });
+ contactActionsDialogFragment.show(getFragmentManager(),
+ CONTACT_ACTIONS_DIALOG_KEY);
+ }
+ return true;
+ }
+ };
+ }
+
+ /** Generates an add contact button */
+ private Preference createAddEmergencyContactPreference() {
+ Preference addEmergencyContact = new Preference(getContext());
+ addEmergencyContact.setTitle(getString(R.string.add_emergency_contact));
+ addEmergencyContact.setOnPreferenceClickListener(new Preference
+ .OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent contactPickerIntent = new Intent(Intent.ACTION_PICK,
+ ContactsContract.Contacts.CONTENT_URI);
+ startActivityForResult(contactPickerIntent, CONTACT_PICKER_RESULT);
+ return true;
+ }
+ });
+ return addEmergencyContact;
+ }
+
+
+ /**
+ * Returns a Set of stored emergency contacts. If editing, make a copy of the set as
+ * described by {@link SharedPreferences#getStringSet(String, Set<String>)}, then call
+ * {@link #setEmergencyContacts(Set)} to store the new contact information.
+ */
+ private Set<String> getEmergencyContacts() {
+ Set<String> emergencyContacts = mSharedPreferences
+ .getStringSet(EMERGENCY_CONTACTS_KEY, Collections.<String>emptySet());
+ return emergencyContacts;
+ }
+
+ private void setEmergencyContacts(Set<String> emergencyContacts) {
+ mSharedPreferences.edit().putStringSet(EMERGENCY_CONTACTS_KEY, emergencyContacts)
+ .commit();
+ }
+}
diff --git a/src/com/android/emergency/ViewInfoActivity.java b/src/com/android/emergency/ViewInfoActivity.java
new file mode 100644
index 0000000..8a3db43
--- /dev/null
+++ b/src/com/android/emergency/ViewInfoActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.emergency;
+
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * Activity for viewing emergency information.
+ */
+public class ViewInfoActivity extends AppCompatPreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+
+ super.onCreate(savedInstanceState);
+ setupActionBar();
+
+ // Create the fragment with readOnly set to true
+ EmergencyInfoFragment emergencyInfoFragment = EmergencyInfoFragment
+ .createEmergencyInfoFragment(true);
+
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction().replace(android.R.id.content,
+ emergencyInfoFragment).commit();
+ }
+}
\ No newline at end of file