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