QRCode Scan: implement embedded qr code scanner

This patch implements the embedded QR Code Scanner. Ring does not
need a third-party application to scan codes anymore.
It relies on a new dependency: com.journeyapps:zxing-android-embedded

Change-Id: I3097ed6302d8c3314cb665cfed6ab34e9dfb4ba0
Tuleap: #558
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index e02ceab..d3799c2 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -255,6 +255,12 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity android:name=".client.QRCodeScannerActivity"
+            android:screenOrientation="fullSensor"
+            android:stateNotNeeded="true"
+            android:theme="@style/zxing_CaptureTheme"
+            android:windowSoftInputMode="stateAlwaysHidden">
+        </activity>
 
         <service
             android:name=".service.LocalService"
diff --git a/ring-android/app/src/main/java/cx/ring/client/QRCodeScannerActivity.java b/ring-android/app/src/main/java/cx/ring/client/QRCodeScannerActivity.java
new file mode 100644
index 0000000..1196c92
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/client/QRCodeScannerActivity.java
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: 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, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package cx.ring.client;
+
+import android.app.Fragment;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.journeyapps.barcodescanner.CaptureActivity;
+
+import cx.ring.R;
+
+public class QRCodeScannerActivity extends CaptureActivity {
+
+    /**
+     * Starts a QR Code scanner and passes the receiver to the engine to notify it with the result.
+     *
+     * @param receiver the Fragment to notify with the result of the scan.
+     */
+    public static void startQRCodeScanWithFragmentReceiver(Fragment receiver) {
+        if (null != receiver) {
+            IntentIntegrator integrator = IntentIntegrator.forFragment(receiver);
+            configureIntentIntegrator(integrator,
+                    receiver.getString(R.string.scan_qr_account_message));
+        }
+    }
+
+    /**
+     * Configures the QR Code scanner with invariable parameters
+     *
+     * @param intentIntegrator the IntentIntegrator to configure
+     * @param promptString     the text to display in the prompt of the QR Code scanner overlay
+     */
+    private static void configureIntentIntegrator(IntentIntegrator intentIntegrator,
+                                                  String promptString) {
+        if (null == intentIntegrator) {
+            return;
+        }
+        intentIntegrator.setPrompt(promptString);
+        intentIntegrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
+        intentIntegrator.setCaptureActivity(QRCodeScannerActivity.class);
+        intentIntegrator.setBarcodeImageEnabled(true);
+        intentIntegrator.setOrientationLocked(false);
+        intentIntegrator.initiateScan();
+    }
+}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
index 8aacb34..34723b8 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
@@ -63,6 +63,7 @@
 import cx.ring.adapters.StarredContactsAdapter;
 import cx.ring.client.ConversationActivity;
 import cx.ring.client.HomeActivity;
+import cx.ring.client.QRCodeScannerActivity;
 import cx.ring.loaders.ContactsLoader;
 import cx.ring.loaders.LoaderConstants;
 import cx.ring.model.CallContact;
@@ -72,8 +73,7 @@
 
 public class SmartListFragment extends Fragment implements SearchView.OnQueryTextListener,
         LoaderManager.LoaderCallbacks<ContactsLoader.Result>,
-        HomeActivity.Refreshable
-{
+        HomeActivity.Refreshable {
     private static final String TAG = SmartListFragment.class.getSimpleName();
 
     private LocalService.Callbacks mCallbacks = LocalService.DUMMY_CALLBACKS;
@@ -135,8 +135,7 @@
 
         if (mSmartListAdapter == null) {
             bindService(getActivity(), service);
-        }
-        else {
+        } else {
             mSmartListAdapter.updateDataset(service.getConversations());
         }
 
@@ -177,7 +176,7 @@
     public void onResume() {
         Log.i(TAG, "onResume");
         super.onResume();
-        ((HomeActivity)getActivity()).setToolbarState(false, R.string.app_name);
+        ((HomeActivity) getActivity()).setToolbarState(false, R.string.app_name);
         refresh();
     }
 
@@ -205,6 +204,7 @@
                 setLoading(false);
                 return true;
             }
+
             @Override
             public boolean onMenuItemActionExpand(MenuItem item) {
                 dialpadMenuItem.setVisible(true);
@@ -257,8 +257,7 @@
                 mCallbacks.getService().clearHistory();
                 return true;
             case R.id.menu_scan_qr:
-                IntentIntegrator integrator = new IntentIntegrator(this);
-                integrator.initiateScan();
+                QRCodeScannerActivity.startQRCodeScanWithFragmentReceiver(this);
             default:
                 return false;
         }
@@ -282,7 +281,7 @@
         b.putString("filter", query);
         getLoaderManager().restartLoader(LoaderConstants.CONTACT_LOADER, b, this);
         newcontact.setVisibility(View.VISIBLE);
-        ((TextView)newcontact.findViewById(R.id.display_name)).setText(/*getString(R.string.contact_call, query)*/query);
+        ((TextView) newcontact.findViewById(R.id.display_name)).setText(/*getString(R.string.contact_call, query)*/query);
         CallContact contact = CallContact.buildUnknown(query);
         newcontact.setTag(contact);
         return true;
@@ -317,7 +316,7 @@
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final CallContact item = (CallContact) parent.getItemAtPosition(position);
-                ((HomeActivity)getActivity()).onTextContact(item);
+                ((HomeActivity) getActivity()).onTextContact(item);
             }
         });
 
@@ -340,7 +339,7 @@
             public void onClick(View v) {
                 CallContact c = (CallContact) newcontact.getTag();
                 if (c != null)
-                    ((HomeActivity)getActivity()).onCallContact(c);
+                    ((HomeActivity) getActivity()).onCallContact(c);
             }
         });
 
@@ -366,7 +365,7 @@
                 service.get40dpContactCache(),
                 service.getThreadPool());
         mListAdapter = new ContactsAdapter(ctx,
-                (HomeActivity)getActivity(),
+                (HomeActivity) getActivity(),
                 service.get40dpContactCache(),
                 service.getThreadPool());
         mGridAdapter = new StarredContactsAdapter(ctx);
@@ -569,12 +568,10 @@
     private void initEmptyTextViewWhileLoading(boolean loading) {
         if (null != this.contactList && this.contactList.getVisibility() == View.VISIBLE) {
             this.mEmptyTextView.setText("");
-        }
-        else  {
+        } else {
             if (loading) {
                 this.mEmptyTextView.setText("");
-            }
-            else {
+            } else {
                 String emptyText = getResources().getQuantityString(R.plurals.home_conferences_title, 0, 0);
                 this.mEmptyTextView.setText(emptyText);
             }
@@ -587,7 +584,8 @@
 
     /**
      * Handles the visibility of some menus to hide / show the overflow menu
-     * @param menu the menu containing the menuitems we need to access
+     *
+     * @param menu    the menu containing the menuitems we need to access
      * @param visible true to display the overflow menu, false otherwise
      */
     private void setOverflowMenuVisible(final Menu menu, boolean visible) {
diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml
index 02091aa..2baebab 100644
--- a/ring-android/app/src/main/res/values/strings.xml
+++ b/ring-android/app/src/main/res/values/strings.xml
@@ -158,4 +158,7 @@
     <!-- Write call log permission -->
     <string name="permission_dialog_write_call_log_message">Ring needs the "Write call log" permission to enable this feature. Please grant it.</string>
 
+    <!-- QRCode Scan -->
+    <string name="scan_qr_account_message">Scan the QR Code of the account you want to add.</string>
+
 </resources>