mvp/injection: Applies MVP and DI patterns to SettingsFragment

- Introduce the SettingsService interface. Its implementation is
the native Android SharePreferences mecanism. Dependency inversion allows
to respect the best practices in terms of separation of concerns.
- LocalService has been adapted to fit the new architecture patterns
but it will be refactored more deeply in a next patch.

Change-Id: Iff9cdc640f2dd6ae2467236564e9d35c3a4d2ca9
Tuleap: #1304
diff --git a/ring-android/app/src/main/java/cx/ring/application/RingApplication.java b/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
index 1e02a83..7430c1b 100644
--- a/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
+++ b/ring-android/app/src/main/java/cx/ring/application/RingApplication.java
@@ -21,6 +21,9 @@
 
 import android.app.Application;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import cx.ring.dependencyinjection.DaggerRingInjectionComponent;
 import cx.ring.dependencyinjection.PresenterInjectionModule;
 import cx.ring.dependencyinjection.RingInjectionComponent;
@@ -31,10 +34,14 @@
 
     private RingInjectionComponent mRingInjectionComponent;
 
+    private Map<String, Boolean> mPermissionsBeingAsked;
+
     @Override
     public void onCreate() {
         super.onCreate();
 
+        mPermissionsBeingAsked = new HashMap<>();
+
         // building injection dependency tree
         mRingInjectionComponent = DaggerRingInjectionComponent.builder()
                 .ringInjectionModule(new RingInjectionModule(this))
@@ -49,4 +56,21 @@
     public RingInjectionComponent getRingInjectionComponent() {
         return mRingInjectionComponent;
     }
+
+    public boolean canAskForPermission (String permission) {
+
+        Boolean isBeingAsked = mPermissionsBeingAsked.get(permission);
+
+        if (isBeingAsked!=null && isBeingAsked) {
+            return false;
+        }
+
+        mPermissionsBeingAsked.put(permission, true);
+
+        return true;
+    }
+
+    public void permissionHasBeenAsked (String permission) {
+        mPermissionsBeingAsked.remove(permission);
+    }
 }
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 f54bfe4..fb48900 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
@@ -57,21 +57,23 @@
 import android.widget.TextView;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 
 import butterknife.BindView;
 import butterknife.ButterKnife;
 import cx.ring.R;
 import cx.ring.about.AboutFragment;
+import cx.ring.application.RingApplication;
 import cx.ring.fragments.AccountsManagementFragment;
 import cx.ring.fragments.ContactListFragment;
-import cx.ring.fragments.SettingsFragment;
 import cx.ring.fragments.SmartListFragment;
 import cx.ring.model.Account;
 import cx.ring.model.CallContact;
 import cx.ring.model.ConfigKey;
 import cx.ring.service.IDRingService;
 import cx.ring.service.LocalService;
+import cx.ring.settings.SettingsFragment;
 import cx.ring.share.ShareFragment;
 import cx.ring.utils.FileUtils;
 import cx.ring.views.MenuHeaderView;
@@ -194,8 +196,16 @@
 
         // Bind to LocalService
         String[] toRequest = LocalService.checkRequiredPermissions(this);
-        if (toRequest.length > 0) {
-            ActivityCompat.requestPermissions(this, toRequest, LocalService.PERMISSIONS_REQUEST);
+        ArrayList<String> permissionsWeCanAsk = new ArrayList<>();
+
+        for (String permission: toRequest) {
+            if (((RingApplication)getApplication()).canAskForPermission(permission)) {
+                permissionsWeCanAsk.add(permission);
+            }
+        }
+
+        if (!permissionsWeCanAsk.isEmpty()) {
+            ActivityCompat.requestPermissions(this, permissionsWeCanAsk.toArray(new String[permissionsWeCanAsk.size()]), LocalService.PERMISSIONS_REQUEST);
         } else if (!mBound) {
             Log.d(TAG, "onCreate: Binding service...");
             Intent intent = new Intent(this, LocalService.class);
@@ -307,7 +317,9 @@
                 }
                 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
                 for (int i = 0, n = permissions.length; i < n; i++) {
-                    switch (permissions[i]) {
+                    String permission = permissions[i];
+                    ((RingApplication)getApplication()).permissionHasBeenAsked(permission);
+                    switch (permission) {
                         case Manifest.permission.RECORD_AUDIO:
                             if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                                 Log.e(TAG, "Missing required permission RECORD_AUDIO");
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/PresenterInjectionModule.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/PresenterInjectionModule.java
index f5975a1..e9509ac 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/PresenterInjectionModule.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/PresenterInjectionModule.java
@@ -23,6 +23,7 @@
 
 import cx.ring.about.AboutPresenter;
 import cx.ring.application.RingApplication;
+import cx.ring.settings.SettingsPresenter;
 import cx.ring.share.SharePresenter;
 import dagger.Module;
 import dagger.Provides;
@@ -53,4 +54,13 @@
         presenter.afterInjection();
         return presenter;
     }
+
+    @Provides
+    @Singleton
+    SettingsPresenter provideSettingsPresenter() {
+        SettingsPresenter presenter = new SettingsPresenter();
+        mRingApplication.getRingInjectionComponent().inject(presenter);
+        presenter.afterInjection();
+        return presenter;
+    }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
index c7fa5cc..4a2c33c 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/RingInjectionComponent.java
@@ -24,6 +24,12 @@
 import cx.ring.about.AboutFragment;
 import cx.ring.about.AboutPresenter;
 import cx.ring.application.RingApplication;
+import cx.ring.service.BootReceiver;
+import cx.ring.service.LocalService;
+import cx.ring.services.HistoryServiceImpl;
+import cx.ring.services.SettingsServiceImpl;
+import cx.ring.settings.SettingsFragment;
+import cx.ring.settings.SettingsPresenter;
 import cx.ring.share.ShareFragment;
 import cx.ring.share.SharePresenter;
 import cx.ring.views.MenuHeaderView;
@@ -40,7 +46,19 @@
 
     void inject(ShareFragment fragment);
 
+    void inject(SettingsFragment fragment);
+
+    void inject(LocalService service);
+
+    void inject(SettingsServiceImpl service);
+
+    void inject(HistoryServiceImpl service);
+
+    void inject(BootReceiver receiver);
+
     void inject(AboutPresenter presenter);
 
     void inject(SharePresenter presenter);
+
+    void inject(SettingsPresenter presenter);
 }
diff --git a/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java b/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
index ec7fd17..e11382f 100755
--- a/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
+++ b/ring-android/app/src/main/java/cx/ring/dependencyinjection/ServiceInjectionModule.java
@@ -22,6 +22,10 @@
 import javax.inject.Singleton;
 
 import cx.ring.application.RingApplication;
+import cx.ring.services.HistoryService;
+import cx.ring.services.HistoryServiceImpl;
+import cx.ring.services.SettingsService;
+import cx.ring.services.SettingsServiceImpl;
 import cx.ring.services.StateService;
 import dagger.Module;
 import dagger.Provides;
@@ -40,4 +44,21 @@
     StateService provideStateService() {
         return new StateService();
     }
+
+    @Provides
+    @Singleton
+    SettingsService provideSettingsService() {
+        SettingsServiceImpl settingsService = new SettingsServiceImpl();
+        mRingApplication.getRingInjectionComponent().inject(settingsService);
+        return settingsService;
+    }
+
+    @Provides
+    @Singleton
+    HistoryService provideHistoryService() {
+        HistoryServiceImpl historyService = new HistoryServiceImpl();
+        mRingApplication.getRingInjectionComponent().inject(historyService);
+        historyService.initHelper();
+        return historyService;
+    }
 }
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
deleted file mode 100644
index 2335454..0000000
--- a/ring-android/app/src/main/java/cx/ring/fragments/SettingsFragment.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
- *
- *  Author:     Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *              Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package cx.ring.fragments;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.Preference;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-import cx.ring.R;
-import cx.ring.client.HomeActivity;
-import cx.ring.service.LocalService;
-
-/**
- * TODO: improvements : handle multiples permissions for feature.
- */
-public class SettingsFragment extends PreferenceFragment implements
-        SharedPreferences.OnSharedPreferenceChangeListener {
-    private static final String TAG = SettingsFragment.class.getSimpleName();
-
-    private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
-
-    private String FEATURE_KEY_PREF_CONTACTS = null;
-    private String FEATURE_KEY_PREF_DIALER = null;
-
-    @Override
-    public void onAttach(Activity activity) {
-        Log.d(TAG, "onAttach");
-        super.onAttach(activity);
-
-        if (!(activity instanceof LocalService.Callbacks)) {
-            throw new IllegalStateException("Activity must implement fragment's callbacks.");
-        }
-
-        mCallbacks = (LocalService.Callbacks) activity;
-    }
-
-    @Override
-    public void onDetach() {
-        Log.d(TAG, "onDetach");
-        super.onDetach();
-        mCallbacks = LocalService.DUMMY_CALLBACKS;
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle bundle, String s) {
-        addPreferencesFromResource(R.xml.preferences);
-        FEATURE_KEY_PREF_CONTACTS = getString(R.string.pref_systemContacts_key);
-        FEATURE_KEY_PREF_DIALER = getString(R.string.pref_systemDialer_key);
-    }
-
-    @Override
-    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);
-        }
-        this.checkAndResolveCorrectSync();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (null != preference) {
-            if (getString(R.string.pref_clearHistory_key).equals(preference.getKey())) {
-                this.clearHistory();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-        String neededPermission = this.neededPermissionForFeature(key);
-        this.handlePermissionsForFeaturePreference(sharedPreferences,
-                key,
-                neededPermission);
-    }
-
-    /**
-     * Check if all the features are in a good state of activation.
-     *
-     * @see SettingsFragment#checkAndResolveCorrectSyncFeatureAndPermission(String)
-     */
-    private void checkAndResolveCorrectSync() {
-        this.checkAndResolveCorrectSyncFeatureAndPermission(FEATURE_KEY_PREF_CONTACTS);
-        this.checkAndResolveCorrectSyncFeatureAndPermission(FEATURE_KEY_PREF_DIALER);
-    }
-
-    /**
-     * Checks if a feature has the correct permission if any. If not, the feature is disable and the
-     * layout reestablished to a proper state.
-     *
-     * @param feature FEATURE_KEY_PREF_CONTACTS or FEATURE_KEY_PREF_DIALER
-     */
-    private void checkAndResolveCorrectSyncFeatureAndPermission(String feature) {
-        if (TextUtils.isEmpty(feature)) {
-            return;
-        }
-        SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
-        boolean useFeature = sharedPreferences.getBoolean(feature, false);
-        String neededPermission = this.neededPermissionForFeature(feature);
-        if (!TextUtils.isEmpty(neededPermission) && useFeature) {
-            boolean hasPermission = LocalService.checkPermission(getActivity(), neededPermission);
-            if (!hasPermission) {
-                this.enableFeature(false, feature);
-            }
-        }
-    }
-
-    /**
-     * Provides the permission associated to a feature
-     *
-     * @param feature FEATURE_KEY_PREF_CONTACTS or FEATURE_KEY_PREF_DIALER
-     * @return the permission as a String
-     */
-    private String neededPermissionForFeature(String feature) {
-        String neededPermission = null;
-        if (FEATURE_KEY_PREF_CONTACTS.equals(feature)) {
-            neededPermission = Manifest.permission.READ_CONTACTS;
-        } else if (FEATURE_KEY_PREF_DIALER.equals(feature)) {
-            neededPermission = Manifest.permission.WRITE_CALL_LOG;
-        }
-        return neededPermission;
-    }
-
-    /**
-     * Handles the permission managements for the key feature of the fragment
-     *
-     * @param sharedPreferences Shared Preferences, such as those from onSharedPreferenceChanged
-     * @param feature           FEATURE_KEY_PREF_CONTACTS or FEATURE_KEY_PREF_DIALER
-     * @param neededPermission  if any, the permission to manage
-     */
-    private void handlePermissionsForFeaturePreference(SharedPreferences sharedPreferences,
-                                                       String feature,
-                                                       String neededPermission) {
-        if (null == sharedPreferences ||
-                TextUtils.isEmpty(feature) ||
-                TextUtils.isEmpty(neededPermission)) {
-            Log.d(TAG, "No permission to handle for feature");
-            return;
-        }
-        //~ Checking if the user wants to use the feature
-        boolean useFeature = sharedPreferences.getBoolean(feature, true);
-        //~ Checking if a permission is required to use the enabled feature
-        if (useFeature && !TextUtils.isEmpty(neededPermission)) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-                //~ Must ask permission to use the feature
-                if (!LocalService.checkPermission(getActivity(),
-                        neededPermission)) {
-                    //~ Ask permission to use the contacts of the device
-                    requestPermissions(new String[]{neededPermission},
-                            LocalService.PERMISSIONS_REQUEST);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode,
-                                           @NonNull String permissions[],
-                                           @NonNull int[] grantResults) {
-        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-        for (int i = 0, n = permissions.length; i < n; ++i) {
-            boolean granted = (grantResults[i] == PackageManager.PERMISSION_GRANTED);
-            switch (permissions[i]) {
-                case Manifest.permission.READ_CONTACTS: {
-                    this.enableFeature(granted, FEATURE_KEY_PREF_CONTACTS);
-                    Activity activity = getActivity();
-                    if (activity instanceof HomeActivity) {
-                        HomeActivity homeActivity = (HomeActivity) getActivity();
-                        homeActivity.getService().refreshContacts();
-                    }
-                }
-                break;
-                case Manifest.permission.WRITE_CALL_LOG: {
-                    this.enableFeature(granted, FEATURE_KEY_PREF_DIALER);
-                }
-                break;
-            }
-        }
-    }
-
-    /**
-     * Enables or disables a feature
-     *
-     * @param enable  boolean true if enabled, false otherwise
-     * @param feature FEATURE_KEY_PREF_CONTACTS or FEATURE_KEY_PREF_DIALER
-     */
-    private void enableFeature(boolean enable, String feature) {
-        if (TextUtils.isEmpty(feature)) {
-            return;
-        }
-        SharedPreferences prefs = getPreferenceScreen().getSharedPreferences();
-        prefs.edit().putBoolean(feature, enable).apply();
-        SwitchPreference pref = (SwitchPreference) findPreference(feature);
-        pref.setChecked(enable);
-        if (!enable) {
-            this.presentPermissionExplanationToastForFeature(feature);
-        }
-    }
-
-    /**
-     * Presents the right explanation toast for the denied permission of the corresponding feature
-     *
-     * @param feature FEATURE_KEY_PREF_CONTACTS or FEATURE_KEY_PREF_DIALER
-     */
-    private void presentPermissionExplanationToastForFeature(String feature) {
-        if (!TextUtils.isEmpty(feature)) {
-            if (feature.equals(FEATURE_KEY_PREF_CONTACTS)) {
-                this.presentReadContactPermissionExplanationToast();
-            } else if (feature.equals(FEATURE_KEY_PREF_DIALER)) {
-                this.presentWriteCallLogPermissionExplanationToast();
-            }
-        }
-    }
-
-    /**
-     * Presents a Toast explaining why the Read Contacts permission is required to display the devi-
-     * ces contacts in Ring.
-     */
-    private void presentReadContactPermissionExplanationToast() {
-        Activity activity = getActivity();
-        if (null != activity) {
-            String toastMessage = getString(R.string.permission_dialog_read_contacts_message);
-            Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show();
-        }
-    }
-
-    /**
-     * Presents a Toast explaining why the Write Call Log permission is required to enable the cor-
-     * responding feature.
-     */
-    private void presentWriteCallLogPermissionExplanationToast() {
-        Activity activity = getActivity();
-        if (null != activity) {
-            String toastMessage = getString(R.string.permission_dialog_write_call_log_message);
-            Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show();
-        }
-    }
-
-    //region History
-
-    /**
-     * Clears all the conversations history. Ask the user to confirm first as it is a critical
-     * action.
-     */
-    private void clearHistory() {
-        final Activity activity = getActivity();
-        if (activity == null || getView() == null)
-            return;
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
-        builder.setTitle(getString(R.string.clear_history_dialog_title))
-                .setMessage(getString(R.string.clear_history_dialog_message))
-                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int id) {
-                        mCallbacks.getService().clearHistory();
-                        Snackbar.make(getView(),
-                                getString(R.string.clear_history_completed),
-                                Snackbar.LENGTH_SHORT).show();
-                    }
-                })
-                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int id) {
-                        //~ Empty
-                    }
-                });
-        AlertDialog dialog = builder.create();
-        dialog.show();
-    }
-
-    //endregion
-}
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryTimeModel.java b/ring-android/app/src/main/java/cx/ring/history/HistoryTimeModel.java
index 14a9035..f513432 100644
--- a/ring-android/app/src/main/java/cx/ring/history/HistoryTimeModel.java
+++ b/ring-android/app/src/main/java/cx/ring/history/HistoryTimeModel.java
@@ -37,7 +37,7 @@
         String NEVER = "Never"; // 24
     }
 
-    private static final String TAG = HistoryManager.class.getSimpleName();
+    private static final String TAG = HistoryTimeModel.class.getSimpleName();
 
     static Calendar removeDays(int ago) {
         Calendar cal = Calendar.getInstance(Locale.getDefault());
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 43c38ee..07a13b6 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
@@ -25,11 +25,9 @@
 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.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -40,7 +38,6 @@
 
 import java.util.ArrayList;
 
-import cx.ring.R;
 import cx.ring.model.CallContact;
 import cx.ring.model.SipUri;
 import cx.ring.service.LocalService;
@@ -79,6 +76,7 @@
     private final Uri baseUri;
     private final LongSparseArray<CallContact> filterFrom;
     private volatile boolean abandon = false;
+    private boolean mCanUseSystemContact = true;
 
     public boolean loadSipContacts = true;
     public boolean loadRingContacts = true;
@@ -93,6 +91,10 @@
         filterFrom = filter;
     }
 
+    public void setSystemContactPermission (boolean canUseSystemContact) {
+        mCanUseSystemContact = canUseSystemContact;
+    }
+
     private boolean checkCancel() {
         return checkCancel(null);
     }
@@ -116,9 +118,7 @@
     public Result loadInBackground()
     {
         final Result res = new Result();
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
-        boolean canUseContacts = sharedPreferences.getBoolean(getContext().getString(R.string.pref_systemContacts_key), true);
-        if (!canUseContacts || !LocalService.checkPermission(getContext(), Manifest.permission.READ_CONTACTS))
+        if (!mCanUseSystemContact || !LocalService.checkPermission(getContext(), Manifest.permission.READ_CONTACTS))
             return res;
 
         long startTime = System.nanoTime();
diff --git a/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java b/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
index 0772b8e..ee90e81 100644
--- a/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
+++ b/ring-android/app/src/main/java/cx/ring/service/BootReceiver.java
@@ -3,28 +3,34 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
 import android.util.Log;
 
-import cx.ring.R;
-import cx.ring.fragments.SettingsFragment;
+import javax.inject.Inject;
+
+import cx.ring.application.RingApplication;
+import cx.ring.services.SettingsService;
 
 public class BootReceiver extends BroadcastReceiver {
     private static final String TAG = BootReceiver.class.getSimpleName();
 
+    @Inject
+    SettingsService mSettingsService;
+
     public BootReceiver() {
     }
 
     @Override
-    public void onReceive(Context c, Intent intent) {
+    public void onReceive(Context context, Intent intent) {
+        
         if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c);
-            boolean startOnBoot = sharedPreferences.getBoolean(c.getString(R.string.pref_startOnBoot_key), true);
-            if (startOnBoot) {
+
+            ((RingApplication) context.getApplicationContext()).getRingInjectionComponent().inject(this);
+            boolean isAllowRingOnStartup = mSettingsService.loadSettings().isAllowRingOnStartup();
+
+            if (isAllowRingOnStartup) {
                 Log.w(TAG, "Starting Ring on boot");
-                Intent serviceIntent = new Intent(c, LocalService.class);
-                c.startService(serviceIntent);
+                Intent serviceIntent = new Intent(context, LocalService.class);
+                context.startService(serviceIntent);
             }
         }
     }
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 88d211c..cf76d1a 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
@@ -69,17 +69,21 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NavigableMap;
+import java.util.Observable;
+import java.util.Observer;
 import java.util.Random;
 import java.util.TreeMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import javax.inject.Inject;
+
 import cx.ring.BuildConfig;
 import cx.ring.R;
+import cx.ring.application.RingApplication;
 import cx.ring.client.ConversationActivity;
 import cx.ring.history.HistoryCall;
 import cx.ring.history.HistoryEntry;
-import cx.ring.history.HistoryManager;
 import cx.ring.history.HistoryText;
 import cx.ring.loaders.AccountsLoader;
 import cx.ring.loaders.ContactsLoader;
@@ -90,12 +94,16 @@
 import cx.ring.model.ConfigKey;
 import cx.ring.model.Conversation;
 import cx.ring.model.SecureSipCall;
+import cx.ring.model.Settings;
 import cx.ring.model.SipCall;
 import cx.ring.model.SipUri;
 import cx.ring.model.TextMessage;
+import cx.ring.services.HistoryService;
+import cx.ring.services.HistoryServiceImpl;
+import cx.ring.services.SettingsService;
 import cx.ring.utils.MediaManager;
 
-public class LocalService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
+public class LocalService extends Service implements Observer {
     static final String TAG = LocalService.class.getSimpleName();
 
     // Emitting events
@@ -117,6 +125,12 @@
 
     public final static String[] REQUIRED_RUNTIME_PERMISSIONS = {Manifest.permission.RECORD_AUDIO};
 
+    @Inject
+    HistoryService mHistoryService;
+
+    @Inject
+    SettingsService mSettingsService;
+
     private IDRingService mService = null;
     private boolean dringStarted = false;
 
@@ -130,8 +144,6 @@
 
     private List<Account> accounts = new ArrayList<>();
 
-    private HistoryManager historyManager;
-
     private final LongSparseArray<CallContact> systemContactCache = new LongSparseArray<>();
     private ContactsLoader.Result lastContactLoaderResult = new ContactsLoader.Result();
 
@@ -260,7 +272,9 @@
             TextMessage message = new TextMessage(false, txt, to, null, account);
             message.setID(id);
             message.read();
-            historyManager.insertNewTextMessage(message);
+            // todo as soon as the HistoryService interface will propose this method we
+            // wont have to cast it anymore
+            ((HistoryServiceImpl) mHistoryService).insertNewTextMessage(message);
             messages.put(id, message);
             textMessageSent(message);
         } catch (RemoteException e) {
@@ -322,7 +336,9 @@
             SipCall call = conf.getParticipants().get(0);
             TextMessage message = new TextMessage(false, txt, call.getNumberUri(), conf.getId(), call.getAccount());
             message.read();
-            historyManager.insertNewTextMessage(message);
+            // todo as soon as the HistoryService interface will propose this method we
+            // wont have to cast it anymore
+            ((HistoryServiceImpl) mHistoryService).insertNewTextMessage(message);
             textMessageSent(message);
         } catch (RemoteException e) {
             Log.e(TAG, "sendTextMessage", e);
@@ -332,7 +348,9 @@
     private void readTextMessage(TextMessage message) {
         message.read();
         HistoryText ht = new HistoryText(message);
-        historyManager.updateTextMessage(ht);
+        // todo as soon as the HistoryService interface will propose this method we
+        // wont have to cast it anymore
+        ((HistoryServiceImpl) mHistoryService).updateTextMessage(ht);
     }
 
     public void readConversation(Conversation conv) {
@@ -432,7 +450,18 @@
             }
         };
 
-        historyManager = new HistoryManager(this);
+        // dependency injection
+        ((RingApplication) getApplication()).getRingInjectionComponent().inject(this);
+
+        // todo 
+        // temporary listen for history modifications
+        // When MVP/DI injection will be done, only the concerned presenters should listen
+        // for model modifications
+        mHistoryService.addObserver(this);
+        mSettingsService.addObserver(this);
+        Settings settings = mSettingsService.loadSettings();
+        canUseContacts = settings.isAllowSystemContacts();
+        canUseMobile = settings.isAllowMobileData();
 
         startDRingService();
 
@@ -441,11 +470,6 @@
         isWifiConn = ni != null && ni.isConnected();
         ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
         isMobileConn = ni != null && ni.isConnected();
-
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-        canUseContacts = sharedPreferences.getBoolean(getString(R.string.pref_systemContacts_key), true);
-        canUseMobile = sharedPreferences.getBoolean(getString(R.string.pref_mobileData_key), false);
-        sharedPreferences.registerOnSharedPreferenceChangeListener(this);
     }
 
     private void startDRingService() {
@@ -466,7 +490,6 @@
     public void onDestroy() {
         super.onDestroy();
         Log.e(TAG, "onDestroy");
-        PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
         stopListener();
         mMemoryCache.evictAll();
         mPool.shutdown();
@@ -536,18 +559,6 @@
         }
     };
 
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-        if (key.equals(getString(R.string.pref_systemContacts_key))) {
-            canUseContacts = sharedPreferences.getBoolean(key, true);
-            mSystemContactLoader.onContentChanged();
-            mSystemContactLoader.startLoading();
-        } else if (key.equals(getString(R.string.pref_mobileData_key))) {
-            canUseMobile = sharedPreferences.getBoolean(key, true);
-            updateConnectivityState();
-        }
-    }
-
     private ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
@@ -557,6 +568,7 @@
             initAccountLoader();
 
             mSystemContactLoader = new ContactsLoader(LocalService.this);
+            mSystemContactLoader.setSystemContactPermission(canUseContacts);
             mSystemContactLoader.registerListener(1, onSystemContactsLoaded);
         }
 
@@ -830,7 +842,7 @@
     }
 
     public void clearHistory() {
-        historyManager.clearDB();
+        mHistoryService.clearHistory();
         refreshConversations();
     }
 
@@ -1084,8 +1096,10 @@
         protected Map<String, Conversation> doInBackground(Void... params) {
             final Map<String, Conversation> ret = new HashMap<>();
             try {
-                final List<HistoryCall> history = historyManager.getAll();
-                final List<HistoryText> historyTexts = historyManager.getAllTextMessages();
+                // todo as soon as the HistoryService interface will propose this method we
+                // wont have to cast it anymore
+                final List<HistoryCall> history = ((HistoryServiceImpl) mHistoryService).getAll();
+                final List<HistoryText> historyTexts = ((HistoryServiceImpl) mHistoryService).getAllTextMessages();
                 final Map<String, ArrayList<String>> confs = mService.getConferenceList();
 
                 for (HistoryCall call : history) {
@@ -1461,7 +1475,10 @@
                     if (conversation.mVisible) {
                         txt.read();
                     }
-                    historyManager.insertNewTextMessage(txt);
+
+                    // todo as soon as the HistoryService interface will propose this method we
+                    // wont have to cast it anymore
+                    ((HistoryServiceImpl) mHistoryService).insertNewTextMessage(txt);
 
                     conversation.addTextMessage(txt);
                     if (!conversation.mVisible) {
@@ -1654,7 +1671,10 @@
                             if (newState == SipCall.State.HUNGUP) {
                                 call.setTimestampEnd(System.currentTimeMillis());
                             }
-                            historyManager.insertNewEntry(found);
+
+                            // todo as soon as the HistoryService interface will propose this method
+                            // we wont have to cast it anymore
+                            ((HistoryServiceImpl) mHistoryService).insertNewEntry(found);
                             conversation.addHistoryCall(new HistoryCall(call));
                             notificationManager.cancel(found.notificationId);
                             found.removeParticipant(call);
@@ -1737,12 +1757,28 @@
 
     public void refreshContacts() {
         Log.d(TAG, "refreshContacts");
+        mSystemContactLoader.setSystemContactPermission(canUseContacts);
         mSystemContactLoader.onContentChanged();
         mSystemContactLoader.startLoading();
     }
 
     public void deleteConversation(Conversation conversation) {
-        historyManager.clearHistoryForConversation(conversation);
+        // todo as soon as the HistoryService interface will propose this method we wont have to cast it anymore
+        ((HistoryServiceImpl) mHistoryService).clearHistoryForConversation(conversation);
         refreshConversations();
     }
+
+    @Override
+    public void update(Observable observable, Object arg) {
+        if (observable instanceof HistoryService) {
+            refreshConversations();
+        }
+
+        if (observable instanceof SettingsService) {
+            canUseContacts = mSettingsService.loadSettings().isAllowSystemContacts();
+            canUseMobile = mSettingsService.loadSettings().isAllowMobileData();
+            refreshContacts();
+            updateConnectivityState();
+        }
+    }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java b/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
index 2692f5c..b10f951 100644
--- a/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
+++ b/ring-android/app/src/main/java/cx/ring/service/OutgoingCallHandler.java
@@ -26,11 +26,9 @@
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.preference.PreferenceManager;
-import android.util.Log;
 
 import cx.ring.R;
 import cx.ring.client.CallActivity;
-import cx.ring.fragments.SettingsFragment;
 import cx.ring.model.SipUri;
 
 public class OutgoingCallHandler extends BroadcastReceiver
diff --git a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java b/ring-android/app/src/main/java/cx/ring/services/HistoryServiceImpl.java
similarity index 83%
rename from ring-android/app/src/main/java/cx/ring/history/HistoryManager.java
rename to ring-android/app/src/main/java/cx/ring/services/HistoryServiceImpl.java
index c0d6ffe..d6c8ea0 100644
--- a/ring-android/app/src/main/java/cx/ring/history/HistoryManager.java
+++ b/ring-android/app/src/main/java/cx/ring/services/HistoryServiceImpl.java
@@ -19,7 +19,7 @@
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-package cx.ring.history;
+package cx.ring.services;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -38,21 +38,27 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.inject.Inject;
+
 import cx.ring.R;
+import cx.ring.history.DatabaseHelper;
+import cx.ring.history.HistoryCall;
+import cx.ring.history.HistoryEntry;
+import cx.ring.history.HistoryText;
 import cx.ring.model.Conference;
 import cx.ring.model.Conversation;
 import cx.ring.model.SipCall;
 import cx.ring.model.TextMessage;
 
-public class HistoryManager {
-    private static final String TAG = HistoryManager.class.getSimpleName();
+public class HistoryServiceImpl extends HistoryService {
+    private static final String TAG = HistoryServiceImpl.class.getSimpleName();
 
-    private Context mContext;
+    @Inject
+    Context mContext;
+
     private DatabaseHelper historyDBHelper = null;
 
-    public HistoryManager(Context context) {
-        mContext = context;
-        getHelper();
+    public HistoryServiceImpl() {
     }
 
     public boolean insertNewEntry(Conference toInsert) {
@@ -80,7 +86,7 @@
 
             HistoryCall persistent = new HistoryCall(call);
             try {
-                Log.w("HistoryManager", "HistoryDao().create() " + persistent.getNumber() + " " + persistent.getStartDate().toString() + " " + persistent.getEndDate());
+                Log.w("HistoryServiceImpl", "HistoryDao().create() " + persistent.getNumber() + " " + persistent.getStartDate().toString() + " " + persistent.getEndDate());
                 getHelper().getHistoryDao().create(persistent);
             } catch (SQLException e) {
                 e.printStackTrace();
@@ -92,7 +98,7 @@
 
     public boolean insertNewTextMessage(HistoryText txt) {
         try {
-            Log.d("HistoryManager", "HistoryDao().create() id:" + txt.id + " acc:" + txt.getAccountID() + " num:" + txt.getNumber() + " date:" + txt.getDate().toString() + " msg:" + txt.getMessage());
+            Log.d("HistoryServiceImpl", "HistoryDao().create() id:" + txt.id + " acc:" + txt.getAccountID() + " num:" + txt.getNumber() + " date:" + txt.getDate().toString() + " msg:" + txt.getMessage());
             getHelper().getTextHistoryDao().create(txt);
         } catch (SQLException e) {
             e.printStackTrace();
@@ -111,7 +117,7 @@
 
     public boolean updateTextMessage(HistoryText txt) {
         try {
-            Log.w("HistoryManager", "HistoryDao().update() id:" + txt.id + " acc:" + txt.getAccountID() + " num:" + txt.getNumber() + " date:" + txt.getDate().toString() + " msg:" + txt.getMessage());
+            Log.w("HistoryServiceImpl", "HistoryDao().update() id:" + txt.id + " acc:" + txt.getAccountID() + " num:" + txt.getNumber() + " date:" + txt.getDate().toString() + " msg:" + txt.getMessage());
             getHelper().getTextHistoryDao().update(txt);
         } catch (SQLException e) {
             e.printStackTrace();
@@ -129,6 +135,15 @@
     }
 
     /**
+     * Init Helper for our DB
+     */
+    public void initHelper() {
+        if (historyDBHelper == null) {
+            historyDBHelper = OpenHelperManager.getHelper(mContext, DatabaseHelper.class);
+        }
+    }
+
+    /**
      * Retrieve helper for our DB
      */
     private DatabaseHelper getHelper() {
@@ -189,14 +204,17 @@
         }
     }
 
-    public boolean clearDB() {
+    @Override
+    public void clearHistory() {
         try {
             TableUtils.clearTable(getHelper().getConnectionSource(), HistoryCall.class);
             TableUtils.clearTable(getHelper().getConnectionSource(), HistoryText.class);
+
+            // notify the observers
+            setChanged();
+            notifyObservers();
         } catch (SQLException e) {
-            e.printStackTrace();
-            return false;
+            Log.e(TAG, "Error while clearing history tables", e);
         }
-        return true;
     }
 }
diff --git a/ring-android/app/src/main/java/cx/ring/services/SettingsServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/SettingsServiceImpl.java
new file mode 100644
index 0000000..a441374
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/services/SettingsServiceImpl.java
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.services;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import javax.inject.Inject;
+
+import cx.ring.model.Settings;
+
+public class SettingsServiceImpl extends SettingsService {
+
+    public static final String RING_SETTINGS = "ring_settings";
+
+    public static final String RING_MOBILE_DATA = "mobile_data";
+    public static final String RING_SYSTEM_CONTACTS = "system_contacts";
+    public static final String RING_PLACE_CALLS = "place_calls";
+    public static final String RING_ON_STARTUP = "on_startup";
+
+    @Inject
+    Context mContext;
+
+    @Override
+    public void saveSettings(Settings settings) {
+        SharedPreferences appPrefs = mContext.getSharedPreferences(RING_SETTINGS, Context.MODE_PRIVATE);
+        SharedPreferences.Editor edit = appPrefs.edit();
+        edit.clear();
+        edit.putBoolean(RING_MOBILE_DATA, settings.isAllowMobileData());
+        edit.putBoolean(RING_SYSTEM_CONTACTS, settings.isAllowSystemContacts());
+        edit.putBoolean(RING_PLACE_CALLS, settings.isAllowPlaceSystemCalls());
+        edit.putBoolean(RING_ON_STARTUP, settings.isAllowRingOnStartup());
+
+        edit.apply();
+
+        // notify the observers
+        setChanged();
+        notifyObservers();
+    }
+
+    @Override
+    public Settings loadSettings() {
+        Settings settings = new Settings();
+        SharedPreferences appPrefs = mContext.getSharedPreferences(RING_SETTINGS, Context.MODE_PRIVATE);
+
+        settings.setAllowMobileData(appPrefs.getBoolean(RING_MOBILE_DATA, false));
+        settings.setAllowSystemContacts(appPrefs.getBoolean(RING_SYSTEM_CONTACTS, true));
+        settings.setAllowPlaceSystemCalls(appPrefs.getBoolean(RING_PLACE_CALLS, false));
+        settings.setAllowRingOnStartup(appPrefs.getBoolean(RING_ON_STARTUP, true));
+
+        return settings;
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java
new file mode 100644
index 0000000..9d1e659
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java
@@ -0,0 +1,257 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author:     Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *              Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package cx.ring.settings;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnCheckedChanged;
+import butterknife.OnClick;
+import butterknife.Unbinder;
+import cx.ring.R;
+import cx.ring.application.RingApplication;
+import cx.ring.client.HomeActivity;
+import cx.ring.model.Settings;
+import cx.ring.mvp.GenericView;
+import cx.ring.service.LocalService;
+
+/**
+ * TODO: improvements : handle multiples permissions for feature.
+ */
+public class SettingsFragment extends Fragment implements GenericView<SettingsViewModel> {
+
+    Unbinder mUnbinder;
+
+    private boolean mIsRefreshingViewFromPresenter;
+
+    @Inject
+    SettingsPresenter mSettingsPresenter;
+
+    @Inject
+    Context mContext;
+
+    @BindView(R.id.settings_mobile_data)
+    Switch mViewMobileData;
+
+    @BindView(R.id.settings_contacts)
+    Switch mViewContacts;
+
+    @BindView(R.id.settings_place_call)
+    Switch mViewPlaceCall;
+
+    @BindView(R.id.settings_startup)
+    Switch mViewStartup;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+        final View inflatedView = inflater.inflate(R.layout.frag_settings, parent, false);
+
+        // views injection
+        mUnbinder = ButterKnife.bind(this, inflatedView);
+
+        // dependency injection
+        ((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
+
+        return inflatedView;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        ((HomeActivity) getActivity()).setToolbarState(false, R.string.menu_item_settings);
+
+        // view binding
+        mSettingsPresenter.bindView(this);
+
+        // loading preferences
+        mSettingsPresenter.loadSettings();
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+
+        // Butterknife unbinding
+        mUnbinder.unbind();
+
+        // view unbinding
+        mSettingsPresenter.unbindView();
+    }
+
+    @OnCheckedChanged({R.id.settings_mobile_data, R.id.settings_contacts, R.id.settings_place_call, R.id.settings_startup})
+    public void onSettingsCheckedChanged(CompoundButton button, boolean isChecked) {
+
+        String neededPermission = null;
+
+        if (isChecked) {
+            switch (button.getId()) {
+                case R.id.settings_contacts:
+                    neededPermission = Manifest.permission.READ_CONTACTS;
+                    break;
+                case R.id.settings_place_call:
+                    neededPermission = Manifest.permission.WRITE_CALL_LOG;
+                    break;
+                default:
+                    neededPermission = null;
+                    break;
+            }
+        }
+
+        // No specific permission needed but we known that user as triggered un settings change
+        if (TextUtils.isEmpty(neededPermission) && !mIsRefreshingViewFromPresenter) {
+            saveSettings();
+            return;
+        }
+
+        // Some specific permissions are required, we must check for them
+        if (!TextUtils.isEmpty(neededPermission)) {
+            if (ContextCompat.checkSelfPermission(mContext, neededPermission) != PackageManager.PERMISSION_GRANTED) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    //~ Ask permission to use the contacts of the device
+                    if (((RingApplication) getActivity().getApplication()).canAskForPermission(neededPermission)) {
+                        requestPermissions(new String[]{neededPermission}, LocalService.PERMISSIONS_REQUEST);
+                    }
+                }
+            } else if (!mIsRefreshingViewFromPresenter){
+                // permission is already granted
+                saveSettings();
+            }
+        }
+    }
+
+    private void saveSettings() {
+        Settings newSettings = new Settings();
+
+        newSettings.setAllowMobileData(mViewMobileData.isChecked());
+        newSettings.setAllowSystemContacts(mViewContacts.isChecked());
+        newSettings.setAllowPlaceSystemCalls(mViewPlaceCall.isChecked());
+        newSettings.setAllowRingOnStartup(mViewStartup.isChecked());
+
+        // save settings according to UI inputs
+        mSettingsPresenter.saveSettings(newSettings);
+    }
+
+    @OnClick(R.id.settings_clear_history)
+    public void onClearHistoryClick() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(getString(R.string.clear_history_dialog_title))
+                .setMessage(getString(R.string.clear_history_dialog_message))
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int id) {
+
+                        // ask the presenter to clear history
+                        mSettingsPresenter.clearHistory();
+
+                        Snackbar.make(getView(),
+                                getString(R.string.clear_history_completed),
+                                Snackbar.LENGTH_SHORT).show();
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int id) {
+                        //~ Empty
+                    }
+                });
+        AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,
+                                           @NonNull String permissions[],
+                                           @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        for (int i = 0, n = permissions.length; i < n; ++i) {
+            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
+                String permission = permissions[i];
+                ((RingApplication) getActivity().getApplication()).permissionHasBeenAsked(permission);
+                switch (permission) {
+                    case Manifest.permission.READ_CONTACTS:
+                        mViewContacts.setChecked(false);
+                        presentReadContactPermissionExplanationToast();
+                        break;
+                    case Manifest.permission.WRITE_CALL_LOG:
+                        mViewPlaceCall.setChecked(false);
+                        presentWriteCallLogPermissionExplanationToast();
+                        break;
+                }
+            }
+        }
+
+        saveSettings();
+    }
+
+    /**
+     * Presents a Toast explaining why the Read Contacts permission is required to display the devi-
+     * ces contacts in Ring.
+     */
+    private void presentReadContactPermissionExplanationToast() {
+        Activity activity = getActivity();
+        if (null != activity) {
+            String toastMessage = getString(R.string.permission_dialog_read_contacts_message);
+            Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    /**
+     * Presents a Toast explaining why the Write Call Log permission is required to enable the cor-
+     * responding feature.
+     */
+    private void presentWriteCallLogPermissionExplanationToast() {
+        Activity activity = getActivity();
+        if (null != activity) {
+            String toastMessage = getString(R.string.permission_dialog_write_call_log_message);
+            Toast.makeText(activity, toastMessage, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    //region View Methods Implementation
+    @Override
+    public void showViewModel(SettingsViewModel viewModel) {
+        mIsRefreshingViewFromPresenter = true;
+        mViewMobileData.setChecked(viewModel.isAllowMobileData());
+        mViewContacts.setChecked(viewModel.isAllowSystemContacts());
+        mViewPlaceCall.setChecked(viewModel.isAllowPlaceSystemCalls());
+        mViewStartup.setChecked(viewModel.isAllowRingOnStartup());
+        mIsRefreshingViewFromPresenter = false;
+    }
+    //endregion
+}
diff --git a/ring-android/app/src/main/res/layout/frag_settings.xml b/ring-android/app/src/main/res/layout/frag_settings.xml
new file mode 100644
index 0000000..16ccef4
--- /dev/null
+++ b/ring-android/app/src/main/res/layout/frag_settings.xml
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (c) 2016 Savoir-faire Linux Inc.
+
+Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="@dimen/padding_medium">
+
+        <!-- Network settings -->
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="8dp"
+            android:text="@string/pref_category_network"
+            android:textColor="@color/color_primary_dark"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp"
+            android:weightSum="1">
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2">
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start"
+                    android:src="@drawable/ic_perm_data_setting_black_24dp"
+                    android:contentDescription="@string/pref_mobileData_summary" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.6"
+                android:orientation="vertical">
+
+                <TextView
+                    style="@style/ListPrimary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:lines="1"
+                    android:text="@string/pref_mobileData_title" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/pref_mobileData_summary" />
+
+            </LinearLayout>
+
+            <Switch
+                android:id="@+id/settings_mobile_data"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2"
+                android:checked="false" />
+
+        </LinearLayout>
+
+        <!-- Contacts settings -->
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="8dp"
+            android:text="@string/pref_category_contacts"
+            android:textColor="@color/color_primary_dark"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp"
+            android:weightSum="1">
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2">
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start"
+                    android:src="@drawable/ic_group_black"
+                    android:contentDescription="@string/pref_systemContacts_summary" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.6"
+                android:orientation="vertical">
+
+                <TextView
+                    style="@style/ListPrimary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:lines="1"
+                    android:text="@string/pref_systemContacts_title" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/pref_systemContacts_summary" />
+
+            </LinearLayout>
+
+            <Switch
+                android:id="@+id/settings_contacts"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2"
+                android:checked="false" />
+
+        </LinearLayout>
+
+        <!-- System settings -->
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="8dp"
+            android:text="@string/pref_category_system"
+            android:textColor="@color/color_primary_dark"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp"
+            android:weightSum="1">
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2">
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start"
+                    android:src="@drawable/ic_dialpad_black"
+                    android:contentDescription="@string/pref_systemDialer_summary" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.6"
+                android:orientation="vertical">
+
+                <TextView
+                    style="@style/ListPrimary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:lines="1"
+                    android:ellipsize="end"
+                    android:text="@string/pref_systemDialer_title" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/pref_systemDialer_summary" />
+
+            </LinearLayout>
+
+            <Switch
+                android:id="@+id/settings_place_call"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2"
+                android:checked="false" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:padding="8dp"
+            android:weightSum="1">
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2">
+
+                <ImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start"
+                    android:src="@drawable/ic_android_black"
+                    android:contentDescription="@string/pref_startOnBoot_summary" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.6"
+                android:orientation="vertical">
+
+                <TextView
+                    style="@style/ListPrimary"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:lines="1"
+                    android:ellipsize="end"
+                    android:text="@string/pref_startOnBoot_title" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/pref_startOnBoot_summary" />
+
+            </LinearLayout>
+
+            <Switch
+                android:id="@+id/settings_startup"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="0.2"
+                android:checked="false" />
+
+        </LinearLayout>
+
+        <!-- Privacy -->
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="8dp"
+            android:text="@string/pref_category_privacy"
+            android:textColor="@color/color_primary_dark"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:id="@+id/settings_clear_history"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="?attr/selectableItemBackground"
+            android:padding="8dp"
+            android:orientation="vertical">
+
+            <TextView
+                style="@style/ListPrimary"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:lines="1"
+                android:ellipsize="end"
+                android:text="@string/pref_clearHistory_title" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/pref_clearHistory_summary" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
diff --git a/ring-android/app/src/main/res/xml/preferences.xml b/ring-android/app/src/main/res/xml/preferences.xml
deleted file mode 100644
index 17896ed..0000000
--- a/ring-android/app/src/main/res/xml/preferences.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-Copyright (c) 2016 Savoir-faire Linux Inc.
-
-Author: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <android.support.v7.preference.PreferenceCategory android:title="@string/pref_category_network">
-        <android.support.v14.preference.SwitchPreference
-            android:defaultValue="false"
-            android:icon="@drawable/ic_perm_data_setting_black_24dp"
-            android:key="@string/pref_mobileData_key"
-            android:summary="@string/pref_mobileData_summary"
-            android:title="@string/pref_mobileData_title" />
-    </android.support.v7.preference.PreferenceCategory>
-
-    <android.support.v7.preference.PreferenceCategory android:title="@string/pref_category_contacts">
-        <android.support.v14.preference.SwitchPreference
-            android:defaultValue="true"
-            android:icon="@drawable/ic_group_black"
-            android:key="@string/pref_systemContacts_key"
-            android:summary="@string/pref_systemContacts_summary"
-            android:title="@string/pref_systemContacts_title" />
-
-    </android.support.v7.preference.PreferenceCategory>
-    <android.support.v7.preference.PreferenceCategory android:title="@string/pref_category_system">
-
-        <android.support.v14.preference.SwitchPreference
-            android:defaultValue="false"
-            android:icon="@drawable/ic_dialpad_black"
-            android:key="@string/pref_systemDialer_key"
-            android:summary="@string/pref_systemDialer_summary"
-            android:title="@string/pref_systemDialer_title" />
-
-
-        <android.support.v14.preference.SwitchPreference
-            android:defaultValue="true"
-            android:icon="@drawable/ic_android_black"
-            android:key="@string/pref_startOnBoot_key"
-            android:summary="@string/pref_startOnBoot_summary"
-            android:title="@string/pref_startOnBoot_title" />
-
-    </android.support.v7.preference.PreferenceCategory>
-
-    <android.support.v7.preference.PreferenceCategory android:title="@string/pref_category_privacy">
-        <Preference
-            android:key="@string/pref_clearHistory_key"
-            android:summary="@string/pref_clearHistory_summary"
-            android:title="@string/pref_clearHistory_title" />
-    </android.support.v7.preference.PreferenceCategory>
-
-</android.support.v7.preference.PreferenceScreen>
\ No newline at end of file
diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/Settings.java b/ring-android/libringclient/src/main/java/cx/ring/model/Settings.java
new file mode 100644
index 0000000..ccdebe6
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/model/Settings.java
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.model;
+
+public class Settings {
+
+    private boolean mAllowMobileData;
+    private boolean mAllowSystemContacts;
+    private boolean mAllowPlaceSystemCalls;
+    private boolean mAllowRingOnStartup;
+
+    public boolean isAllowMobileData() {
+        return mAllowMobileData;
+    }
+
+    public void setAllowMobileData(boolean allowMobileData) {
+        this.mAllowMobileData = allowMobileData;
+    }
+
+    public boolean isAllowSystemContacts() {
+        return mAllowSystemContacts;
+    }
+
+    public void setAllowSystemContacts(boolean allowSystemContacts) {
+        this.mAllowSystemContacts = allowSystemContacts;
+    }
+
+    public boolean isAllowPlaceSystemCalls() {
+        return mAllowPlaceSystemCalls;
+    }
+
+    public void setAllowPlaceSystemCalls(boolean allowPlaceSystemCalls) {
+        this.mAllowPlaceSystemCalls = allowPlaceSystemCalls;
+    }
+
+    public boolean isAllowRingOnStartup() {
+        return mAllowRingOnStartup;
+    }
+
+    public void setAllowRingOnStartup(boolean allowRingOnStartup) {
+        this.mAllowRingOnStartup = allowRingOnStartup;
+    }
+}
diff --git a/ring-android/libringclient/src/main/java/cx/ring/services/HistoryService.java b/ring-android/libringclient/src/main/java/cx/ring/services/HistoryService.java
new file mode 100644
index 0000000..327d3cc
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/services/HistoryService.java
@@ -0,0 +1,28 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.services;
+
+import java.util.Observable;
+
+public abstract class HistoryService extends Observable {
+
+    public abstract void clearHistory();
+
+}
diff --git a/ring-android/libringclient/src/main/java/cx/ring/services/SettingsService.java b/ring-android/libringclient/src/main/java/cx/ring/services/SettingsService.java
new file mode 100644
index 0000000..37f5b90
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/services/SettingsService.java
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.services;
+
+import java.util.Observable;
+
+import cx.ring.model.Settings;
+
+public abstract class SettingsService extends Observable {
+
+    public abstract void saveSettings(Settings settings);
+
+    public abstract Settings loadSettings();
+
+}
diff --git a/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsPresenter.java b/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsPresenter.java
new file mode 100644
index 0000000..a64e795
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsPresenter.java
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.settings;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.inject.Inject;
+
+import cx.ring.model.Settings;
+import cx.ring.mvp.GenericView;
+import cx.ring.mvp.RootPresenter;
+import cx.ring.services.HistoryService;
+import cx.ring.services.SettingsService;
+
+public class SettingsPresenter extends RootPresenter<GenericView<SettingsViewModel>> implements Observer {
+
+    @Inject
+    SettingsService mSettingsService;
+
+    @Inject
+    HistoryService mHistoryService;
+
+    @Override
+    public void afterInjection() {
+        // We observe the application settings changes
+        mSettingsService.addObserver(this);
+        // no need to observe the history changes
+        // only the smartlist should do so
+    }
+
+    public void loadSettings() {
+        if (getView() == null) {
+            return;
+        }
+
+        // load the app settings
+        Settings settings = mSettingsService.loadSettings();
+
+        // let the view display the associated ViewModel
+        getView().showViewModel(new SettingsViewModel(settings));
+    }
+
+    public void saveSettings (Settings settings) {
+        mSettingsService.saveSettings(settings);
+    }
+
+    public void clearHistory() {
+        mHistoryService.clearHistory();
+    }
+
+    @Override
+    public void update(Observable observable, Object o) {
+        loadSettings();
+    }
+}
diff --git a/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsViewModel.java b/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsViewModel.java
new file mode 100644
index 0000000..2ce2827
--- /dev/null
+++ b/ring-android/libringclient/src/main/java/cx/ring/settings/SettingsViewModel.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.settings;
+
+import cx.ring.model.Settings;
+
+public class SettingsViewModel {
+
+    Settings mSettings;
+
+    public SettingsViewModel(Settings settings) {
+        mSettings = settings;
+    }
+
+    public boolean isAllowMobileData() {
+        return mSettings.isAllowMobileData();
+    }
+
+    public boolean isAllowSystemContacts() {
+        return mSettings.isAllowSystemContacts();
+    }
+
+    public boolean isAllowPlaceSystemCalls() {
+        return mSettings.isAllowPlaceSystemCalls();
+    }
+
+    public boolean isAllowRingOnStartup() {
+        return mSettings.isAllowRingOnStartup();
+    }
+}