preferences: add Settings page

* Allow user to select system contact integration
* Allow user to enable mobile connectivity

Tuleap: #102
Change-Id: Ieb161d97d5dc61b3664597def130dea6c7eefb5f
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index fa0667c..f0e0ef6 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -160,6 +160,15 @@
             android:theme="@style/AppThemeWithoutOverlayCompat"
             android:windowSoftInputMode="adjustResize" >
         </activity>
+        <activity
+            android:name=".client.SettingsActivity"
+            android:label="@string/menu_item_settings"
+            android:theme="@style/AppThemeWithoutOverlayCompat">
+            <intent-filter>
+                <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
 
         <service
             android:name=".service.LocalService"
diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
index 88cf9b5..4d5cd76 100644
--- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java
@@ -37,6 +37,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import android.Manifest;
 import android.app.Activity;
 import android.app.AlertDialog;
 import cx.ring.R;
@@ -47,6 +48,7 @@
 import cx.ring.fragments.DialingFragment;
 import cx.ring.fragments.HistoryFragment;
 import cx.ring.fragments.MenuFragment;
+import cx.ring.fragments.SettingsFragment;
 import cx.ring.history.HistoryEntry;
 import cx.ring.model.CallContact;
 import cx.ring.model.account.Account;
@@ -66,10 +68,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
@@ -227,30 +231,55 @@
         switch (requestCode) {
             case LocalService.PERMISSIONS_REQUEST: {
                 if (grantResults.length == 0) {
-                    finish();
                     return;
                 }
-                for (int grantResult : grantResults) {
-                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
-                        finish();
-                        return;
+
+                for (int i=0, n=permissions.length; i<n; i++) {
+                    switch (permissions[i]) {
+                        case Manifest.permission.RECORD_AUDIO:
+                            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
+                                Log.e(TAG, "Missing required permission RECORD_AUDIO");
+                                AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                                        .setTitle(R.string.start_error_title)
+                                        .setMessage(R.string.start_error_mic_required)
+                                        .setIcon(R.drawable.ic_mic_black_48dp)
+                                        .setCancelable(false)
+                                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                                            @Override
+                                            public void onClick(DialogInterface dialog, int which) {
+                                                finish();
+                                            }
+                                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
+                                            @Override
+                                            public void onCancel(DialogInterface dialog) {
+                                                finish();
+                                            }
+                                        });
+                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                                        @Override
+                                        public void onDismiss(DialogInterface dialog) {
+                                            finish();
+                                        }
+                                    });
+                                }
+                                builder.show();
+                                return;
+                            }
+                            break;
+                        case Manifest.permission.READ_CONTACTS:
+                            SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+                            sharedPref.edit().putBoolean(SettingsFragment.KEY_PREF_CONTACTS, grantResults[i] == PackageManager.PERMISSION_GRANTED).apply();
+                            break;
                     }
                 }
-                // If request is cancelled, the result arrays are empty.
-                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                    Log.w(TAG, "onRequestPermissionsResult granted");
 
-                    if (!mBound) {
-                        Intent intent = new Intent(this, LocalService.class);
-                        startService(intent);
-                        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
-                    }
-
-                } else {
-                    Log.w(TAG, "onRequestPermissionsResult denied");
-                    // permission denied, boo! Disable the
-                    // functionality that depends on this permission.
+                if (!mBound) {
+                    Intent intent = new Intent(this, LocalService.class);
+                    startService(intent);
+                    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
                 }
+
                 break;
             }
         }
@@ -710,6 +739,12 @@
                 fContent = new AboutFragment();
                 getFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).replace(R.id.main_frame, fContent, "About").addToBackStack("About").commit();
                 break;
+            case R.id.menuitem_prefs:
+                if (fContent instanceof SettingsFragment)
+                    break;
+                fContent = new SettingsFragment();
+                getFragmentManager().beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).replace(R.id.main_frame, fContent, "Prefs").addToBackStack("Prefs").commit();
+                break;
             default:
                 return false;
         }
diff --git a/ring-android/app/src/main/java/cx/ring/client/SettingsActivity.java b/ring-android/app/src/main/java/cx/ring/client/SettingsActivity.java
new file mode 100644
index 0000000..eeb0c11
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/client/SettingsActivity.java
@@ -0,0 +1,30 @@
+package cx.ring.client;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+
+import cx.ring.fragments.SettingsFragment;
+
+public class SettingsActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, new SettingsFragment())
+                .commit();
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SettingsFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SettingsFragment.java
new file mode 100644
index 0000000..57a0a43
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SettingsFragment.java
@@ -0,0 +1,77 @@
+package cx.ring.fragments;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v4.app.ActivityCompat;
+
+import cx.ring.R;
+import cx.ring.client.HomeActivity;
+import cx.ring.service.LocalService;
+
+public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
+{
+    public static final String KEY_PREF_CONTACTS = "pref_systemContacts";
+    public static final String KEY_PREF_MOBILE = "pref_mobileData";
+
+    @Override
+    public void onCreatePreferences(Bundle bundle, String s) {
+        addPreferencesFromResource(R.xml.preferences);
+    }
+
+    public void onResume() {
+        super.onResume();
+        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+        Activity activity = getActivity();
+        if (activity instanceof HomeActivity)
+            ((HomeActivity) activity).setToolbarState(false, R.string.menu_item_settings);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (key.equals(KEY_PREF_CONTACTS)) {
+            boolean val = sharedPreferences.getBoolean(KEY_PREF_CONTACTS, true);
+            if (val && !LocalService.checkPermission(getActivity(), Manifest.permission.READ_CONTACTS)) {
+                ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_CONTACTS}, LocalService.PERMISSIONS_REQUEST);
+            }
+        }
+    }
+
+    private void updateContactPreference() {
+        SharedPreferences prefs = getPreferenceScreen().getSharedPreferences();
+        boolean val = prefs.getBoolean(KEY_PREF_CONTACTS, true);
+        if (val && !LocalService.checkPermission(getActivity(), Manifest.permission.READ_CONTACTS)) {
+            prefs.edit().putBoolean(KEY_PREF_CONTACTS, false).apply();
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+        switch (requestCode) {
+            case LocalService.PERMISSIONS_REQUEST: {
+                if (grantResults.length == 0) {
+                    updateContactPreference();
+                    return;
+                }
+                for (int grantResult : grantResults) {
+                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
+                        updateContactPreference();
+                        return;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+}
diff --git a/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java b/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
index 1e9f1ee..cefb52b 100644
--- a/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
+++ b/ring-android/app/src/main/java/cx/ring/loaders/ContactsLoader.java
@@ -23,6 +23,7 @@
 
 import java.util.ArrayList;
 
+import cx.ring.fragments.SettingsFragment;
 import cx.ring.model.CallContact;
 import cx.ring.model.SipUri;
 import cx.ring.service.LocalService;
@@ -31,9 +32,11 @@
 import android.content.AsyncTaskLoader;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.OperationCanceledException;
+import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
@@ -109,11 +112,13 @@
     @Override
     public Result loadInBackground()
     {
-        long startTime = System.nanoTime();
         final Result res = new Result();
-        if (!LocalService.checkPermission(getContext(), Manifest.permission.READ_CONTACTS))
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+        boolean canUseContacts = sharedPreferences.getBoolean(SettingsFragment.KEY_PREF_CONTACTS, true);
+        if (!canUseContacts || !LocalService.checkPermission(getContext(), Manifest.permission.READ_CONTACTS))
             return res;
 
+        long startTime = System.nanoTime();
         ContentResolver cr = getContext().getContentResolver();
         if (baseUri != null) {
             Cursor result = cr.query(baseUri, CONTACTS_ID_PROJECTION, SELECT, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
diff --git a/ring-android/app/src/main/java/cx/ring/service/LocalService.java b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
index 108517d..d893105 100644
--- a/ring-android/app/src/main/java/cx/ring/service/LocalService.java
+++ b/ring-android/app/src/main/java/cx/ring/service/LocalService.java
@@ -32,6 +32,7 @@
 import android.content.IntentFilter;
 import android.content.Loader;
 import android.content.ServiceConnection;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.database.Cursor;
@@ -43,6 +44,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.preference.PreferenceManager;
 import android.provider.Contacts;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -68,6 +70,7 @@
 import java.util.concurrent.Executors;
 
 import cx.ring.BuildConfig;
+import cx.ring.fragments.SettingsFragment;
 import cx.ring.history.HistoryCall;
 import cx.ring.history.HistoryEntry;
 import cx.ring.history.HistoryManager;
@@ -85,7 +88,7 @@
 import cx.ring.model.account.AccountDetailTls;
 
 
-public class LocalService extends Service
+public class LocalService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener
 {
     static final String TAG = LocalService.class.getSimpleName();
     static public final String ACTION_CONF_UPDATE = BuildConfig.APPLICATION_ID + ".action.CONF_UPDATE";
@@ -95,7 +98,7 @@
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
     public static final int PERMISSIONS_REQUEST = 57;
 
-    public final static String[] REQUIRED_RUNTIME_PERMISSIONS = {Manifest.permission.READ_CONTACTS, Manifest.permission.RECORD_AUDIO};
+    public final static String[] REQUIRED_RUNTIME_PERMISSIONS = {Manifest.permission.RECORD_AUDIO};
 
     private IDRingService mService = null;
     private final ContactsContentObserver contactContentObserver = new ContactsContentObserver();
@@ -122,6 +125,9 @@
     private boolean isWifiConn = false;
     private boolean isMobileConn = false;
 
+    private boolean canUseContacts = true;
+    private boolean canUseMobile = false;
+
     public ContactsLoader.Result getSortedContacts() {
         Log.w(TAG, "getSortedContacts " + lastContactLoaderResult.contacts.size() + " contacts, " + lastContactLoaderResult.starred.size() + " starred.");
         return lastContactLoaderResult;
@@ -140,7 +146,7 @@
     }
 
     public boolean isConnected() {
-        return isWifiConn || isMobileConn;
+        return isWifiConn || (canUseMobile && isMobileConn);
     }
     public boolean isWifiConnected() {
         return isWifiConn;
@@ -215,6 +221,11 @@
         isWifiConn = ni != null && ni.isConnected();
         ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
         isMobileConn = ni != null && ni.isConnected();
+
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        canUseContacts = sharedPreferences.getBoolean(SettingsFragment.KEY_PREF_CONTACTS, true);
+        canUseMobile = sharedPreferences.getBoolean(SettingsFragment.KEY_PREF_MOBILE, true);
+        sharedPreferences.registerOnSharedPreferenceChangeListener(this);
     }
 
     @Override
@@ -227,6 +238,7 @@
     public void onDestroy() {
         Log.e(TAG, "onDestroy");
         super.onDestroy();
+        PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
         stopListener();
         mMemoryCache.evictAll();
         mPool.shutdown();
@@ -265,6 +277,21 @@
         }
     };
 
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        switch (key) {
+            case SettingsFragment.KEY_PREF_CONTACTS:
+                canUseContacts = sharedPreferences.getBoolean(key, true);
+                mSystemContactLoader.onContentChanged();
+                mSystemContactLoader.startLoading();
+                break;
+            case SettingsFragment.KEY_PREF_MOBILE:
+                canUseMobile = sharedPreferences.getBoolean(key, true);
+                updateConnectivityState();
+                break;
+        }
+    }
+
     private ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
@@ -347,6 +374,10 @@
             if (!checkPermission(c, p))
                 perms.add(p);
         }
+        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(c);
+        boolean contact_perm = sharedPref.getBoolean(SettingsFragment.KEY_PREF_CONTACTS, true);
+        if (contact_perm && !checkPermission(c, Manifest.permission.READ_CONTACTS))
+            perms.add(Manifest.permission.READ_CONTACTS);
         return perms.toArray(new String[perms.size()]);
     }
 
@@ -424,7 +455,7 @@
             if (conv.contact.hasNumber(number))
                 return conv.contact;
         }
-        return findContactByNumber(getContentResolver(), number);
+        return canUseContacts ? findContactByNumber(getContentResolver(), number) : CallContact.buildUnknown(number);
     }
 
     public CallContact findContactById(long id) {
@@ -707,7 +738,7 @@
             number = CallContact.canonicalNumber(number);
             CallContact c = cache.get(number);
             if (c == null) {
-                c = findContactByNumber(cr, number);
+                c = canUseContacts ? findContactByNumber(cr, number) : CallContact.buildUnknown(number);
                 //if (c != null)
                 cache.put(number, c);
             }
@@ -744,7 +775,7 @@
                     } else {
                         contact = localContactCache.get(call.getContactID());
                         if (contact == null) {
-                            contact = findById(cr, call.getContactID());
+                            contact = canUseContacts ? findById(cr, call.getContactID()) : CallContact.buildUnknown(call.getNumber());
                             if (contact != null)
                                 contact.addPhoneNumber(call.getNumber());
                             else {
@@ -792,7 +823,7 @@
                     } else {
                         contact = localContactCache.get(htext.getContactID());
                         if (contact == null) {
-                            contact = findById(cr, htext.getContactID());
+                            contact = canUseContacts ? findById(cr, htext.getContactID()) : CallContact.buildUnknown(htext.getNumber());
                             if (contact != null)
                                 contact.addPhoneNumber(htext.getNumber());
                             else {
@@ -941,7 +972,7 @@
         isMobileConn = ni != null && ni.isConnected();
 
         try {
-            getRemoteService().setAccountsActive(isWifiConn);
+            getRemoteService().setAccountsActive(isConnected());
         } catch (RemoteException e) {
             e.printStackTrace();
         }
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_group_black_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_group_black_24dp.png
new file mode 100644
index 0000000..0782166
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_group_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_mic_black_48dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_mic_black_48dp.png
new file mode 100644
index 0000000..ad8299e
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_mic_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png
new file mode 100644
index 0000000..1643436
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_perm_data_setting_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png b/ring-android/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
new file mode 100644
index 0000000..acf1ddf
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_group_black_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_group_black_24dp.png
new file mode 100644
index 0000000..e2600a4
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_group_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_mic_black_48dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_mic_black_48dp.png
new file mode 100644
index 0000000..cac51c3
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_mic_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_perm_data_setting_black_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_perm_data_setting_black_24dp.png
new file mode 100644
index 0000000..6686406
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_perm_data_setting_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png b/ring-android/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
new file mode 100644
index 0000000..c59419c
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_group_black_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_group_black_24dp.png
new file mode 100644
index 0000000..c2e9ffe
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_group_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png
new file mode 100644
index 0000000..cf70b63
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_perm_data_setting_black_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_perm_data_setting_black_24dp.png
new file mode 100644
index 0000000..a8a97f2
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_perm_data_setting_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png b/ring-android/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
new file mode 100644
index 0000000..e84e188
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_group_black_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_group_black_24dp.png
new file mode 100644
index 0000000..5a8b5d0
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_group_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png
new file mode 100644
index 0000000..0b214ab
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_perm_data_setting_black_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_perm_data_setting_black_24dp.png
new file mode 100644
index 0000000..d9e712d
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_perm_data_setting_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png b/ring-android/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
new file mode 100644
index 0000000..3023ff8
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_group_black_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_group_black_24dp.png
new file mode 100644
index 0000000..2994e7c
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_group_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png
new file mode 100644
index 0000000..e365a0f
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_perm_data_setting_black_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_perm_data_setting_black_24dp.png
new file mode 100644
index 0000000..a451d53
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_perm_data_setting_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
new file mode 100644
index 0000000..476d5c9
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png
Binary files differ
diff --git a/ring-android/app/src/main/res/menu/drawer.xml b/ring-android/app/src/main/res/menu/drawer.xml
index 0c0a18b..2c2cc83 100644
--- a/ring-android/app/src/main/res/menu/drawer.xml
+++ b/ring-android/app/src/main/res/menu/drawer.xml
@@ -11,6 +11,10 @@
             android:icon="@drawable/ic_group_black_48dp"
             android:title="@string/menu_item_accounts"/>
         <item
+            android:id="@+id/menuitem_prefs"
+            android:icon="@drawable/ic_settings_black_24dp"
+            android:title="@string/menu_item_settings"/>
+        <item
             android:id="@+id/menuitem_about"
             android:icon="@drawable/ic_info_black_48dp"
             android:title="@string/menu_item_about"/>
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index f4cf563..17496e2 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -122,5 +122,8 @@
     <string name="ongoing_call">Ongoing call</string>
     <string name="hist_in_call">Incoming call of %1$s</string>
     <string name="hist_out_call">Outgoing call of %1$s</string>
+    <string name="add_to_contacts">Add to contacts</string>
+    <string name="start_error_title">Can\'t start Ring !</string>
+    <string name="start_error_mic_required">Ring requires the microphone permission to work.</string>
 
 </resources>
diff --git a/ring-android/app/src/main/res/values/styles.xml b/ring-android/app/src/main/res/values/styles.xml
index e45bed5..4d7f03a 100644
--- a/ring-android/app/src/main/res/values/styles.xml
+++ b/ring-android/app/src/main/res/values/styles.xml
@@ -7,6 +7,7 @@
         <item name="colorPrimaryDark">@color/color_primary_dark</item>
         <item name="android:windowActionBarOverlay">true</item>
         <item name="windowActionBarOverlay">true</item>
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
     </style>
 
     <style name="AppThemeWithOverlay" parent="AppThemeBase">
@@ -31,6 +32,11 @@
         <item name="colorPrimary">@color/color_primary_light</item>
         <item name="colorPrimaryDark">@color/color_primary_dark</item>
         <item name="android:actionBarStyle">@style/NativeActionBar</item>
+        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
+    </style>
+
+    <style name="AppThemeWithoutOverlayCompatNoShadow" parent="AppThemeWithoutOverlayCompat">
+        <item name="android:windowContentOverlay">@null</item>
     </style>
 
     <style name="NativeActionBar" parent="@android:style/Widget.DeviceDefault.Light.ActionBar.Solid.Inverse">
diff --git a/ring-android/app/src/main/res/xml/preferences.xml b/ring-android/app/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..cc8be1b
--- /dev/null
+++ b/ring-android/app/src/main/res/xml/preferences.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <PreferenceCategory android:title="Network">
+        <android.support.v14.preference.SwitchPreference
+            android:key="pref_mobileData"
+            android:title="Mobile data"
+            android:summary="Allow Ring on 3G/LTE networks additionally to Wi-Fi"
+            android:defaultValue="false"
+            android:icon="@drawable/ic_perm_data_setting_black_24dp"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="Contacts">
+        <android.support.v14.preference.SwitchPreference
+            android:key="pref_systemContacts"
+            android:title="Use system contacts"
+            android:summary="Use system contacts to show caller details."
+            android:defaultValue="true"
+            android:icon="@drawable/ic_group_black_24dp"/>
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
\ No newline at end of file