瀏覽代碼

Add support for QR codes & deep links

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
tobiasKaminsky 6 年之前
父節點
當前提交
d47c77ca5c

+ 1 - 0
build.gradle

@@ -234,6 +234,7 @@ dependencies {
     implementation 'com.afollestad:sectioned-recyclerview:0.5.0'
     implementation 'com.github.chrisbanes:PhotoView:2.3.0'
     implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
+    implementation 'com.github.tobiaskaminsky:qrcodescanner:0.1.2.2' // 'com.github.blikoon:QRCodeScanner:0.1.2'
 
     implementation 'org.parceler:parceler-api:1.1.12'
     annotationProcessor 'org.parceler:parceler:1.1.12'

+ 247 - 0
drawable_resources/qrcode.svg

@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg version="1.1" baseProfile="full" width="232" height="232" viewBox="0 0 232 232" xmlns="http://www.w3.org/2000/svg"
+    xmlns:xlink="http://www.w3.org/1999/xlink">
+    <desc></desc>
+    <rect width="232" height="232" fill="#ffffff" cx="0" cy="0" />
+    <defs>
+        <rect id="p" width="8" height="8" />
+    </defs>
+    <g fill="#000000">
+        <use x="32" y="32" xlink:href="#p" />
+        <use x="32" y="40" xlink:href="#p" />
+        <use x="32" y="48" xlink:href="#p" />
+        <use x="32" y="56" xlink:href="#p" />
+        <use x="32" y="64" xlink:href="#p" />
+        <use x="32" y="72" xlink:href="#p" />
+        <use x="32" y="80" xlink:href="#p" />
+        <use x="32" y="96" xlink:href="#p" />
+        <use x="32" y="104" xlink:href="#p" />
+        <use x="32" y="112" xlink:href="#p" />
+        <use x="32" y="120" xlink:href="#p" />
+        <use x="32" y="128" xlink:href="#p" />
+        <use x="32" y="144" xlink:href="#p" />
+        <use x="32" y="152" xlink:href="#p" />
+        <use x="32" y="160" xlink:href="#p" />
+        <use x="32" y="168" xlink:href="#p" />
+        <use x="32" y="176" xlink:href="#p" />
+        <use x="32" y="184" xlink:href="#p" />
+        <use x="32" y="192" xlink:href="#p" />
+        <use x="40" y="32" xlink:href="#p" />
+        <use x="40" y="80" xlink:href="#p" />
+        <use x="40" y="96" xlink:href="#p" />
+        <use x="40" y="104" xlink:href="#p" />
+        <use x="40" y="144" xlink:href="#p" />
+        <use x="40" y="192" xlink:href="#p" />
+        <use x="48" y="32" xlink:href="#p" />
+        <use x="48" y="48" xlink:href="#p" />
+        <use x="48" y="56" xlink:href="#p" />
+        <use x="48" y="64" xlink:href="#p" />
+        <use x="48" y="80" xlink:href="#p" />
+        <use x="48" y="96" xlink:href="#p" />
+        <use x="48" y="104" xlink:href="#p" />
+        <use x="48" y="120" xlink:href="#p" />
+        <use x="48" y="128" xlink:href="#p" />
+        <use x="48" y="144" xlink:href="#p" />
+        <use x="48" y="160" xlink:href="#p" />
+        <use x="48" y="168" xlink:href="#p" />
+        <use x="48" y="176" xlink:href="#p" />
+        <use x="48" y="192" xlink:href="#p" />
+        <use x="56" y="32" xlink:href="#p" />
+        <use x="56" y="48" xlink:href="#p" />
+        <use x="56" y="56" xlink:href="#p" />
+        <use x="56" y="64" xlink:href="#p" />
+        <use x="56" y="80" xlink:href="#p" />
+        <use x="56" y="96" xlink:href="#p" />
+        <use x="56" y="104" xlink:href="#p" />
+        <use x="56" y="112" xlink:href="#p" />
+        <use x="56" y="128" xlink:href="#p" />
+        <use x="56" y="144" xlink:href="#p" />
+        <use x="56" y="160" xlink:href="#p" />
+        <use x="56" y="168" xlink:href="#p" />
+        <use x="56" y="176" xlink:href="#p" />
+        <use x="56" y="192" xlink:href="#p" />
+        <use x="64" y="32" xlink:href="#p" />
+        <use x="64" y="48" xlink:href="#p" />
+        <use x="64" y="56" xlink:href="#p" />
+        <use x="64" y="64" xlink:href="#p" />
+        <use x="64" y="80" xlink:href="#p" />
+        <use x="64" y="96" xlink:href="#p" />
+        <use x="64" y="104" xlink:href="#p" />
+        <use x="64" y="112" xlink:href="#p" />
+        <use x="64" y="120" xlink:href="#p" />
+        <use x="64" y="144" xlink:href="#p" />
+        <use x="64" y="160" xlink:href="#p" />
+        <use x="64" y="168" xlink:href="#p" />
+        <use x="64" y="176" xlink:href="#p" />
+        <use x="64" y="192" xlink:href="#p" />
+        <use x="72" y="32" xlink:href="#p" />
+        <use x="72" y="80" xlink:href="#p" />
+        <use x="72" y="104" xlink:href="#p" />
+        <use x="72" y="120" xlink:href="#p" />
+        <use x="72" y="144" xlink:href="#p" />
+        <use x="72" y="192" xlink:href="#p" />
+        <use x="80" y="32" xlink:href="#p" />
+        <use x="80" y="40" xlink:href="#p" />
+        <use x="80" y="48" xlink:href="#p" />
+        <use x="80" y="56" xlink:href="#p" />
+        <use x="80" y="64" xlink:href="#p" />
+        <use x="80" y="72" xlink:href="#p" />
+        <use x="80" y="80" xlink:href="#p" />
+        <use x="80" y="96" xlink:href="#p" />
+        <use x="80" y="112" xlink:href="#p" />
+        <use x="80" y="128" xlink:href="#p" />
+        <use x="80" y="144" xlink:href="#p" />
+        <use x="80" y="152" xlink:href="#p" />
+        <use x="80" y="160" xlink:href="#p" />
+        <use x="80" y="168" xlink:href="#p" />
+        <use x="80" y="176" xlink:href="#p" />
+        <use x="80" y="184" xlink:href="#p" />
+        <use x="80" y="192" xlink:href="#p" />
+        <use x="88" y="96" xlink:href="#p" />
+        <use x="88" y="104" xlink:href="#p" />
+        <use x="88" y="112" xlink:href="#p" />
+        <use x="88" y="128" xlink:href="#p" />
+        <use x="96" y="40" xlink:href="#p" />
+        <use x="96" y="56" xlink:href="#p" />
+        <use x="96" y="72" xlink:href="#p" />
+        <use x="96" y="80" xlink:href="#p" />
+        <use x="96" y="96" xlink:href="#p" />
+        <use x="96" y="136" xlink:href="#p" />
+        <use x="96" y="144" xlink:href="#p" />
+        <use x="96" y="160" xlink:href="#p" />
+        <use x="96" y="168" xlink:href="#p" />
+        <use x="96" y="176" xlink:href="#p" />
+        <use x="96" y="184" xlink:href="#p" />
+        <use x="96" y="192" xlink:href="#p" />
+        <use x="104" y="40" xlink:href="#p" />
+        <use x="104" y="56" xlink:href="#p" />
+        <use x="104" y="64" xlink:href="#p" />
+        <use x="104" y="88" xlink:href="#p" />
+        <use x="104" y="120" xlink:href="#p" />
+        <use x="104" y="128" xlink:href="#p" />
+        <use x="104" y="136" xlink:href="#p" />
+        <use x="104" y="144" xlink:href="#p" />
+        <use x="104" y="152" xlink:href="#p" />
+        <use x="104" y="160" xlink:href="#p" />
+        <use x="104" y="168" xlink:href="#p" />
+        <use x="104" y="176" xlink:href="#p" />
+        <use x="112" y="32" xlink:href="#p" />
+        <use x="112" y="40" xlink:href="#p" />
+        <use x="112" y="48" xlink:href="#p" />
+        <use x="112" y="80" xlink:href="#p" />
+        <use x="112" y="104" xlink:href="#p" />
+        <use x="112" y="112" xlink:href="#p" />
+        <use x="112" y="128" xlink:href="#p" />
+        <use x="112" y="144" xlink:href="#p" />
+        <use x="112" y="184" xlink:href="#p" />
+        <use x="112" y="192" xlink:href="#p" />
+        <use x="120" y="32" xlink:href="#p" />
+        <use x="120" y="48" xlink:href="#p" />
+        <use x="120" y="72" xlink:href="#p" />
+        <use x="120" y="96" xlink:href="#p" />
+        <use x="120" y="104" xlink:href="#p" />
+        <use x="120" y="120" xlink:href="#p" />
+        <use x="120" y="144" xlink:href="#p" />
+        <use x="120" y="160" xlink:href="#p" />
+        <use x="120" y="168" xlink:href="#p" />
+        <use x="120" y="184" xlink:href="#p" />
+        <use x="128" y="32" xlink:href="#p" />
+        <use x="128" y="40" xlink:href="#p" />
+        <use x="128" y="48" xlink:href="#p" />
+        <use x="128" y="56" xlink:href="#p" />
+        <use x="128" y="64" xlink:href="#p" />
+        <use x="128" y="80" xlink:href="#p" />
+        <use x="128" y="104" xlink:href="#p" />
+        <use x="128" y="112" xlink:href="#p" />
+        <use x="128" y="120" xlink:href="#p" />
+        <use x="128" y="128" xlink:href="#p" />
+        <use x="128" y="136" xlink:href="#p" />
+        <use x="128" y="168" xlink:href="#p" />
+        <use x="128" y="176" xlink:href="#p" />
+        <use x="128" y="184" xlink:href="#p" />
+        <use x="128" y="192" xlink:href="#p" />
+        <use x="136" y="96" xlink:href="#p" />
+        <use x="136" y="104" xlink:href="#p" />
+        <use x="136" y="112" xlink:href="#p" />
+        <use x="136" y="120" xlink:href="#p" />
+        <use x="136" y="136" xlink:href="#p" />
+        <use x="136" y="168" xlink:href="#p" />
+        <use x="136" y="184" xlink:href="#p" />
+        <use x="144" y="32" xlink:href="#p" />
+        <use x="144" y="40" xlink:href="#p" />
+        <use x="144" y="48" xlink:href="#p" />
+        <use x="144" y="56" xlink:href="#p" />
+        <use x="144" y="64" xlink:href="#p" />
+        <use x="144" y="72" xlink:href="#p" />
+        <use x="144" y="80" xlink:href="#p" />
+        <use x="144" y="128" xlink:href="#p" />
+        <use x="144" y="168" xlink:href="#p" />
+        <use x="144" y="176" xlink:href="#p" />
+        <use x="144" y="184" xlink:href="#p" />
+        <use x="152" y="32" xlink:href="#p" />
+        <use x="152" y="80" xlink:href="#p" />
+        <use x="152" y="96" xlink:href="#p" />
+        <use x="152" y="136" xlink:href="#p" />
+        <use x="152" y="152" xlink:href="#p" />
+        <use x="152" y="168" xlink:href="#p" />
+        <use x="152" y="192" xlink:href="#p" />
+        <use x="160" y="32" xlink:href="#p" />
+        <use x="160" y="48" xlink:href="#p" />
+        <use x="160" y="56" xlink:href="#p" />
+        <use x="160" y="64" xlink:href="#p" />
+        <use x="160" y="80" xlink:href="#p" />
+        <use x="160" y="104" xlink:href="#p" />
+        <use x="160" y="120" xlink:href="#p" />
+        <use x="160" y="128" xlink:href="#p" />
+        <use x="160" y="136" xlink:href="#p" />
+        <use x="160" y="160" xlink:href="#p" />
+        <use x="160" y="168" xlink:href="#p" />
+        <use x="160" y="184" xlink:href="#p" />
+        <use x="168" y="32" xlink:href="#p" />
+        <use x="168" y="48" xlink:href="#p" />
+        <use x="168" y="56" xlink:href="#p" />
+        <use x="168" y="64" xlink:href="#p" />
+        <use x="168" y="80" xlink:href="#p" />
+        <use x="168" y="96" xlink:href="#p" />
+        <use x="168" y="104" xlink:href="#p" />
+        <use x="168" y="112" xlink:href="#p" />
+        <use x="168" y="120" xlink:href="#p" />
+        <use x="168" y="128" xlink:href="#p" />
+        <use x="168" y="152" xlink:href="#p" />
+        <use x="168" y="160" xlink:href="#p" />
+        <use x="168" y="168" xlink:href="#p" />
+        <use x="168" y="184" xlink:href="#p" />
+        <use x="176" y="32" xlink:href="#p" />
+        <use x="176" y="48" xlink:href="#p" />
+        <use x="176" y="56" xlink:href="#p" />
+        <use x="176" y="64" xlink:href="#p" />
+        <use x="176" y="80" xlink:href="#p" />
+        <use x="176" y="104" xlink:href="#p" />
+        <use x="176" y="112" xlink:href="#p" />
+        <use x="176" y="120" xlink:href="#p" />
+        <use x="176" y="144" xlink:href="#p" />
+        <use x="176" y="152" xlink:href="#p" />
+        <use x="176" y="168" xlink:href="#p" />
+        <use x="176" y="176" xlink:href="#p" />
+        <use x="176" y="184" xlink:href="#p" />
+        <use x="184" y="32" xlink:href="#p" />
+        <use x="184" y="80" xlink:href="#p" />
+        <use x="184" y="96" xlink:href="#p" />
+        <use x="184" y="112" xlink:href="#p" />
+        <use x="184" y="128" xlink:href="#p" />
+        <use x="184" y="144" xlink:href="#p" />
+        <use x="184" y="152" xlink:href="#p" />
+        <use x="184" y="192" xlink:href="#p" />
+        <use x="192" y="32" xlink:href="#p" />
+        <use x="192" y="40" xlink:href="#p" />
+        <use x="192" y="48" xlink:href="#p" />
+        <use x="192" y="56" xlink:href="#p" />
+        <use x="192" y="64" xlink:href="#p" />
+        <use x="192" y="72" xlink:href="#p" />
+        <use x="192" y="80" xlink:href="#p" />
+        <use x="192" y="104" xlink:href="#p" />
+        <use x="192" y="120" xlink:href="#p" />
+        <use x="192" y="128" xlink:href="#p" />
+        <use x="192" y="160" xlink:href="#p" />
+    </g>
+</svg>

+ 0 - 1
src/debug/AndroidManifest.xml

@@ -14,6 +14,5 @@
 
     <application
         android:testOnly="false"
-        android:allowBackup="false"
         tools:ignore="GoogleAppIndexingWarning"/>
 </manifest>

+ 16 - 1
src/main/AndroidManifest.xml

@@ -52,6 +52,7 @@
     <!-- WRITE_EXTERNAL_STORAGE may be enabled or disabled by the user after installation in
         API >= 23; the app needs to handle this -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CAMERA" />
 
     <!-- Next permissions are always approved in installation time, the apps needs to do nothing special in runtime -->
     <uses-permission android:name="android.permission.INTERNET" />
@@ -70,6 +71,7 @@
     must request the FOREGROUND_SERVICE permission -->
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
+    <uses-sdk tools:overrideLibrary="com.blikoon.qrcodescanner" />
 
     <application
         android:name=".MainApp"
@@ -274,11 +276,24 @@
 
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".authentication.DeepLinkLoginActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:exported="true"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:theme="@style/Theme.ownCloud.noActionBar.Login">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
+
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="@string/login_data_own_scheme" android:host="login"/>
+
+                <data
+                    android:host="login"
+                    android:scheme="@string/login_data_own_scheme" />
             </intent-filter>
         </activity>
 

+ 178 - 70
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -40,6 +40,7 @@
 
 package com.owncloud.android.authentication;
 
+import android.Manifest;
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.annotation.SuppressLint;
@@ -50,6 +51,7 @@ import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -83,6 +85,7 @@ import android.widget.ProgressBar;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
+import com.blikoon.qrcodescanner.QrCodeActivity;
 import com.google.android.material.snackbar.Snackbar;
 import com.google.android.material.textfield.TextInputLayout;
 import com.owncloud.android.MainApp;
@@ -110,6 +113,7 @@ import com.owncloud.android.operations.GetServerInfoOperation;
 import com.owncloud.android.operations.OAuth2GetAccessToken;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.activity.FirstRunActivity;
 import com.owncloud.android.ui.components.CustomEditText;
 import com.owncloud.android.ui.dialog.CredentialsDialogFragment;
@@ -119,6 +123,7 @@ import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ErrorMessageAdapter;
+import com.owncloud.android.utils.PermissionUtil;
 
 import java.io.InputStream;
 import java.net.URLDecoder;
@@ -127,6 +132,7 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
@@ -191,6 +197,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     public static final int NO_ICON = 0;
     public static final String EMPTY_STRING = "";
 
+    private static final int REQUEST_CODE_QR_SCAN = 101;
+
+
     /// parameters from EXTRAs in starter Intent
     private byte mAction;
     private Account mAccount;
@@ -261,7 +270,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         //Log_OC.e(TAG,  "onCreate init");
         super.onCreate(savedInstanceState);
 
-        if (savedInstanceState == null) {
+        Uri data = getIntent().getData();
+        boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme));
+        if (savedInstanceState == null && !directLogin) {
             FirstRunActivity.runIfNeeded(this);
         }
 
@@ -320,21 +331,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             /// initialize general UI elements
             initOverallUi();
 
-            findViewById(R.id.centeredRefreshButton).setOnClickListener(new View.OnClickListener() {
-
-                @Override
-                public void onClick(View v) {
-                    checkOcServer();
-                }
-            });
-
-            findViewById(R.id.embeddedRefreshButton).setOnClickListener(new View.OnClickListener() {
+            findViewById(R.id.centeredRefreshButton).setOnClickListener(v -> checkOcServer());
 
-                @Override
-                public void onClick(View v) {
-                    checkOcServer();
-                }
-            });
+            findViewById(R.id.embeddedRefreshButton).setOnClickListener(v -> checkOcServer());
 
             /// initialize block to be moved to single Fragment to check server and get info about it
 
@@ -404,41 +403,35 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
         // show snackbar after 60s to switch back to old login method
         if (showLegacyLogin) {
-            new Handler().postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    DisplayUtils.createSnackbar(mLoginWebView, R.string.fallback_weblogin_text, Snackbar.LENGTH_INDEFINITE)
-                            .setActionTextColor(getResources().getColor(R.color.primary_dark))
-                            .setAction(R.string.fallback_weblogin_back, new View.OnClickListener() {
-                                @Override
-                                public void onClick(View v) {
-                                    mLoginWebView.setVisibility(View.INVISIBLE);
-                                    webViewLoginMethod = false;
-
-                                    setContentView(R.layout.account_setup);
-
-                                    // initialize general UI elements
-                                    initOverallUi();
-
-                                    mPasswordInputLayout.setVisibility(View.VISIBLE);
-                                    mUsernameInputLayout.setVisibility(View.VISIBLE);
-                                    mUsernameInput.requestFocus();
-                                    mOAuth2Check.setVisibility(View.INVISIBLE);
-                                    mAuthStatusView.setVisibility(View.INVISIBLE);
-                                    mServerStatusView.setVisibility(View.INVISIBLE);
-                                    mTestServerButton.setVisibility(View.INVISIBLE);
-                                    forceOldLoginMethod = true;
-                                    mOkButton.setVisibility(View.VISIBLE);
-
-                                    initServerPreFragment(null);
-
-                                    mHostUrlInput.setText(baseURL);
-
-                                    checkOcServer();
-                                }
-                            }).show();
-                }
-            }, 60 * 1000);
+            new Handler().postDelayed(() -> DisplayUtils.createSnackbar(mLoginWebView,
+                                                                        R.string.fallback_weblogin_text,
+                                                                        Snackbar.LENGTH_INDEFINITE)
+                .setActionTextColor(getResources().getColor(R.color.primary_dark))
+                .setAction(R.string.fallback_weblogin_back, v -> {
+                    mLoginWebView.setVisibility(View.INVISIBLE);
+                    webViewLoginMethod = false;
+
+                    setContentView(R.layout.account_setup);
+
+                    // initialize general UI elements
+                    initOverallUi();
+
+                    mPasswordInputLayout.setVisibility(View.VISIBLE);
+                    mUsernameInputLayout.setVisibility(View.VISIBLE);
+                    mUsernameInput.requestFocus();
+                    mOAuth2Check.setVisibility(View.INVISIBLE);
+                    mAuthStatusView.setVisibility(View.INVISIBLE);
+                    mServerStatusView.setVisibility(View.INVISIBLE);
+                    mTestServerButton.setVisibility(View.INVISIBLE);
+                    forceOldLoginMethod = true;
+                    mOkButton.setVisibility(View.VISIBLE);
+
+                    initServerPreFragment(null);
+
+                    mHostUrlInput.setText(baseURL);
+
+                    checkOcServer();
+                }).show(), 60 * 1000);
         }
     }
 
@@ -512,9 +505,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString);
 
         if (loginUrlInfo != null) {
-            mServerInfo.mBaseUrl = AuthenticatorUrlUtils.normalizeUrlSuffix(loginUrlInfo.serverAddress);
-            webViewUser = loginUrlInfo.username;
-            webViewPassword = loginUrlInfo.password;
+            try {
+                mServerInfo.mBaseUrl = AuthenticatorUrlUtils.normalizeUrlSuffix(loginUrlInfo.serverAddress);
+                webViewUser = loginUrlInfo.username;
+                webViewPassword = loginUrlInfo.password;
+            } catch (Exception e) {
+                mServerStatusIcon = R.drawable.ic_alert;
+                mServerStatusText = "QR Code could not be read!";
+                showServerStatus();
+            }
             checkOcServer();
         }
     }
@@ -583,14 +582,19 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     }
 
     private void initAuthTokenType() {
-        mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
+        Bundle extras = getIntent().getExtras();
+        mAuthTokenType = null;
+
+        if (extras != null) {
+            extras.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
+        }
+
         if (mAuthTokenType == null) {
             if (mAccount != null) {
                 boolean oAuthRequired = mAccountMgr.getUserData(mAccount, Constants.KEY_SUPPORTS_OAUTH2) != null;
                 boolean samlWebSsoRequired = mAccountMgr.getUserData
                         (mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
                 mAuthTokenType = chooseAuthTokenType(oAuthRequired, samlWebSsoRequired);
-
             } else {
                 boolean oAuthSupported = AUTH_ON.equals(getString(R.string.auth_method_oauth2));
                 boolean samlWebSsoSupported = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
@@ -627,6 +631,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         mOkButton = findViewById(R.id.buttonOK);
         mOkButton.setOnClickListener(v -> onOkClick());
 
+        findViewById(R.id.scanQR).setOnClickListener(v -> onScan());
+
         setupInstructionMessage();
 
         mTestServerButton.setVisibility(mAction == ACTION_CREATE ? View.VISIBLE : View.GONE);
@@ -763,17 +769,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
             // TODO find out if this is really necessary, or if it can done in a different way
-            findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
-                @Override
-                public boolean onTouch(View view, MotionEvent event) {
-                    if (event.getAction() == MotionEvent.ACTION_DOWN &&
-                            AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(
-                                    MainApp.getAccountType(getBaseContext())).equals(mAuthTokenType) &&
-                            mHostUrlInput.hasFocus()) {
-                        checkOcServer();
-                    }
-                    return false;
+            findViewById(R.id.scroll).setOnTouchListener((view, event) -> {
+                if (event.getAction() == MotionEvent.ACTION_DOWN &&
+                    AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(
+                        MainApp.getAccountType(getBaseContext())).equals(mAuthTokenType) &&
+                    mHostUrlInput.hasFocus()) {
+                    checkOcServer();
                 }
+                return false;
             });
         }
     }
@@ -980,6 +983,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mNewCapturedUriFromOAuth2Redirection = data;
         }
 
+        if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) {
+            parseAndLoginFromWebView(data.toString());
+        }
+
         if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) {
             webViewLoginMethod = true;
             setContentView(R.layout.account_setup_webview);
@@ -1757,6 +1764,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
             if (success) {
                 finish();
+
+                AccountUtils.setCurrentOwnCloudAccount(this, mAccount.name);
+
+                Intent i = new Intent(this, FileDisplayActivity.class);
+                i.setAction(FileDisplayActivity.RESTART);
+                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                startActivity(i);
+
             } else {
                 // init webView again
                 if (mLoginWebView != null) {
@@ -1804,9 +1819,31 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {    // authorization fail due to client side - probably wrong credentials
             if (webViewLoginMethod) {
                 mLoginWebView = findViewById(R.id.login_webview);
-                initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, true, false);
 
-                DisplayUtils.showSnackMessage(this, mLoginWebView, R.string.auth_access_failed, result.getLogMessage());
+                if (mLoginWebView != null) {
+                    initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, true, false);
+                    DisplayUtils.showSnackMessage(this, mLoginWebView, R.string.auth_access_failed,
+                                                  result.getLogMessage());
+                } else {
+                    DisplayUtils.showSnackMessage(this, R.string.auth_access_failed, result.getLogMessage());
+
+                    // init webView again
+                    if (mLoginWebView != null) {
+                        mLoginWebView.setVisibility(View.GONE);
+                    }
+                    setContentView(R.layout.account_setup);
+
+                    initOverallUi();
+
+                    CustomEditText serverAddressField = findViewById(R.id.hostUrlInput);
+                    serverAddressField.setText(mServerInfo.mBaseUrl);
+
+                    findViewById(R.id.oauth_onOff_check).setVisibility(View.GONE);
+                    findViewById(R.id.server_status_text).setVisibility(View.GONE);
+                    mAuthStatusView = findViewById(R.id.auth_status_text);
+
+                    showAuthStatus();
+                }
             } else {
                 updateAuthStatusIconAndText(result);
                 showAuthStatus();
@@ -1978,6 +2015,40 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
     }
 
+    public void onScan() {
+        if (PermissionUtil.checkSelfPermission(this, Manifest.permission.CAMERA)) {
+            startQRScanner();
+        } else {
+            PermissionUtil.requestCameraPermission(this);
+        }
+    }
+
+    private void startQRScanner() {
+        Intent i = new Intent(AuthenticatorActivity.this, QrCodeActivity.class);
+        startActivityForResult(i, REQUEST_CODE_QR_SCAN);
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
+                                           @NonNull int[] grantResults) {
+        switch (requestCode) {
+            case PermissionUtil.PERMISSIONS_CAMERA: {
+                // If request is cancelled, result arrays are empty.
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    // permission was granted
+                    startQRScanner();
+                } else {
+                    // permission denied
+                    return;
+                }
+                return;
+            }
+
+            default:
+                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
+
     /**
     /**
      * Updates the content and visibility state of the icon and text associated
@@ -2256,10 +2327,27 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             )) {
                 mOperationsServiceBinder = (OperationsServiceBinder) service;
 
-                doOnResumeAndBound();
-
+                Uri data = getIntent().getData();
+                if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) {
+                    String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/";
+                    LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, data.toString());
+
+                    if (loginUrlInfo != null) {
+                        try {
+                            mServerInfo.mBaseUrl = AuthenticatorUrlUtils.normalizeUrlSuffix(loginUrlInfo.serverAddress);
+                            webViewUser = loginUrlInfo.username;
+                            webViewPassword = loginUrlInfo.password;
+                            doOnResumeAndBound();
+                        } catch (Exception e) {
+                            mServerStatusIcon = R.drawable.ic_alert;
+                            mServerStatusText = "QR Code could not be read!";
+                            showServerStatus();
+                        }
+                    }
+                } else {
+                    doOnResumeAndBound();
+                }
             }
-
         }
 
         @Override
@@ -2302,4 +2390,24 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     public void doNegativeAuthenticationDialogClick() {
         mIsFirstAuthAttempt = true;
     }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        if (requestCode == REQUEST_CODE_QR_SCAN) {
+            if (data == null) {
+                return;
+            }
+
+            String result = data.getStringExtra("com.blikoon.qrcodescanner.got_qr_scan_relult");
+
+            if (!result.startsWith(getString(R.string.login_data_own_scheme))) {
+                mServerStatusIcon = R.drawable.ic_alert;
+                mServerStatusText = "QR Code could not be read!";
+                showServerStatus();
+                return;
+            }
+
+            parseAndLoginFromWebView(result);
+        }
+    }
 }

+ 29 - 0
src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.java

@@ -0,0 +1,29 @@
+package com.owncloud.android.authentication;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import com.owncloud.android.R;
+import com.owncloud.android.utils.ThemeUtils;
+
+public class DeepLinkLoginActivity extends AuthenticatorActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.deep_link_login);
+
+        Uri data = getIntent().getData();
+
+        if (data != null) {
+            String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/";
+            LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, data.toString());
+
+            TextView loginText = findViewById(R.id.loginInfo);
+            loginText.setTextColor(ThemeUtils.fontColor(this));
+            loginText.setText(String.format("Login with %1$s to %2$s", loginUrlInfo.username,
+                                            loginUrlInfo.serverAddress));
+        }
+    }
+}

+ 1 - 2
src/main/java/com/owncloud/android/services/OperationsService.java

@@ -740,8 +740,7 @@ public class OperationsService extends Service {
             final RemoteOperation operation, final RemoteOperationResult result
     ) {
         int count = 0;
-        Iterator<OnRemoteOperationListener> listeners =
-                mOperationsBinder.mBoundListeners.keySet().iterator();
+        Iterator<OnRemoteOperationListener> listeners = mOperationsBinder.mBoundListeners.keySet().iterator();
         while (listeners.hasNext()) {
             final OnRemoteOperationListener listener = listeners.next();
             final Handler handler = mOperationsBinder.mBoundListeners.get(listener);

+ 12 - 0
src/main/java/com/owncloud/android/utils/PermissionUtil.java

@@ -15,6 +15,7 @@ public final class PermissionUtil {
     public static final int PERMISSIONS_READ_CONTACTS_AUTOMATIC = 2;
     public static final int PERMISSIONS_READ_CONTACTS_MANUALLY = 3;
     public static final int PERMISSIONS_WRITE_CONTACTS = 4;
+    public static final int PERMISSIONS_CAMERA = 5;
 
     private PermissionUtil() {
         // utility class -> private constructor
@@ -57,4 +58,15 @@ public final class PermissionUtil {
                 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                 PERMISSIONS_WRITE_EXTERNAL_STORAGE);
     }
+
+    /**
+     * request camera permission.
+     *
+     * @param activity The target activity.
+     */
+    public static void requestCameraPermission(Activity activity) {
+        ActivityCompat.requestPermissions(activity,
+                                          new String[]{Manifest.permission.CAMERA},
+                                          PERMISSIONS_CAMERA);
+    }
 }

+ 717 - 0
src/main/res/drawable/ic_qrcode.xml

@@ -0,0 +1,717 @@
+<vector android:height="72dp"
+    android:viewportHeight="232"
+    android:viewportWidth="232"
+    android:width="72dp"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M0,0h232v232h-232z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M32,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M40,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M48,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M56,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M64,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M72,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M80,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M88,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M88,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M88,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M88,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M96,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,88h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M104,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M112,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M120,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M128,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M136,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M144,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M152,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,136h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M160,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,160h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M168,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,168h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,176h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M176,184h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,96h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,112h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,144h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,152h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M184,192h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,32h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,40h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,48h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,56h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,64h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,72h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,80h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,104h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,120h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,128h8v8h-8z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M192,160h8v8h-8z" />
+</vector>

+ 9 - 0
src/main/res/layout-land/account_setup.xml

@@ -296,4 +296,13 @@
             app:cornerRadius="@dimen/button_corner_radius"/>
 	</LinearLayout>
 
+    <ImageButton
+        android:id="@+id/scanQR"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_qrcode"
+        android:theme="@style/Button.Login"
+        android:background="@color/transparent"
+        android:contentDescription="@string/scanQR_description" />
+
 </RelativeLayout>

+ 9 - 0
src/main/res/layout/account_setup.xml

@@ -277,6 +277,15 @@
             app:cornerRadius="@dimen/button_corner_radius"/>
         </LinearLayout>
 
+        <ImageButton
+            android:id="@+id/scanQR"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_qrcode"
+            android:theme="@style/Button.Login"
+            android:background="@color/transparent"
+            android:contentDescription="@string/scanQR_description" />
+
     </LinearLayout>
 
 </ScrollView>

+ 62 - 0
src/main/res/layout/deep_link_login.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Nextcloud Android client application
+
+  @author Tobias Kaminsky
+  Copyright (C) 2018 Tobias Kaminsky
+  Copyright (C) 2018 Nextcloud GmbH.
+
+  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 <https://www.gnu.org/licenses/>.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/scroll"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center"
+    android:fillViewport="true"
+    android:orientation="vertical">
+
+    <!-- The main content view -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/thumbnail"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:contentDescription="@null"
+            app:srcCompat="@drawable/logo" />
+
+        <TextView
+            android:id="@+id/loginInfo"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="middle"
+            android:text="@string/placeholder_filename"
+            android:textColor="@color/black"
+            android:textSize="20sp"
+            android:textStyle="bold" />
+
+        <ProgressBar
+            android:id="@+id/progressBar"
+            style="?android:attr/progressBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/standard_margin"
+            android:indeterminate="true" />
+    </LinearLayout>
+</ScrollView>

+ 1 - 0
src/main/res/values/strings.xml

@@ -849,4 +849,5 @@
     <string name="error_starting_direct_camera_upload">Error starting camera</string>
     <string name="uploader_upload_files_behaviour_not_writable">source folder is read-only; file will only be uploaded</string>
     <string name="auto_upload_file_behaviour_kept_in_folder">kept in original folder, as it is readonly</string>
+    <string name="scanQR_description">Login via QR code</string>
 </resources>

+ 1 - 1
src/test/java/com/owncloud/android/authentication/AuthenticatorDataUrlTest.java

@@ -33,7 +33,7 @@ import org.junit.runners.BlockJUnit4ClassRunner;
 public class AuthenticatorDataUrlTest {
     private static final String URL_PARSING = " url parsing";
     private static final String INCORRECT_USER_VALUE_IN = "Incorrect user value in ";
-    private String schemeUrl = "nextcloud://login/";
+    private String schemeUrl = "nc://login/";
     private String plus = "&";
 
     private String userValue = "testuser123";