UriHandlerActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.Manifest;
  4import android.app.Activity;
  5import android.content.Intent;
  6import android.content.pm.PackageManager;
  7import android.net.Uri;
  8import android.os.Build;
  9import android.os.Bundle;
 10import android.support.v4.content.ContextCompat;
 11import android.support.v7.app.AppCompatActivity;
 12import android.widget.Toast;
 13
 14import com.google.common.base.Strings;
 15
 16import java.util.List;
 17import java.util.regex.Matcher;
 18import java.util.regex.Pattern;
 19
 20import eu.siacs.conversations.R;
 21import eu.siacs.conversations.persistance.DatabaseBackend;
 22import eu.siacs.conversations.services.QuickConversationsService;
 23import eu.siacs.conversations.utils.ProvisioningUtils;
 24import eu.siacs.conversations.utils.SignupUtils;
 25import eu.siacs.conversations.utils.XmppUri;
 26import eu.siacs.conversations.xmpp.Jid;
 27
 28public class UriHandlerActivity extends AppCompatActivity {
 29
 30    public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
 31    private static final String EXTRA_ALLOW_PROVISIONING = "extra_allow_provisioning";
 32    private static final int REQUEST_SCAN_QR_CODE = 0x1234;
 33    private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
 34    private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION = 0x6790;
 35    private static final Pattern V_CARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
 36    private boolean handled = false;
 37
 38    public static void scan(final Activity activity) {
 39        scan(activity, false);
 40    }
 41
 42    public static void scan(final Activity activity, final boolean provisioning) {
 43        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
 44            final Intent intent = new Intent(activity, UriHandlerActivity.class);
 45            intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
 46            if (provisioning) {
 47                intent.putExtra(EXTRA_ALLOW_PROVISIONING, true);
 48            }
 49            intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
 50            activity.startActivity(intent);
 51        } else {
 52            activity.requestPermissions(
 53                    new String[]{Manifest.permission.CAMERA},
 54                    provisioning ? REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION : REQUEST_CAMERA_PERMISSIONS_TO_SCAN
 55            );
 56        }
 57    }
 58
 59    public static void onRequestPermissionResult(Activity activity, int requestCode, int[] grantResults) {
 60        if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN && requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
 61            return;
 62        }
 63        if (grantResults.length > 0) {
 64            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 65                if (requestCode == REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
 66                    scan(activity, true);
 67                } else {
 68                    scan(activity);
 69                }
 70            } else {
 71                Toast.makeText(activity, R.string.qr_code_scanner_needs_access_to_camera, Toast.LENGTH_SHORT).show();
 72            }
 73        }
 74    }
 75
 76    @Override
 77    protected void onCreate(Bundle savedInstanceState) {
 78        super.onCreate(savedInstanceState);
 79        this.handled = savedInstanceState != null && savedInstanceState.getBoolean("handled", false);
 80        getLayoutInflater().inflate(R.layout.toolbar, findViewById(android.R.id.content));
 81        setSupportActionBar(findViewById(R.id.toolbar));
 82    }
 83
 84    @Override
 85    public void onStart() {
 86        super.onStart();
 87        handleIntent(getIntent());
 88    }
 89
 90    @Override
 91    public void onSaveInstanceState(Bundle savedInstanceState) {
 92        savedInstanceState.putBoolean("handled", this.handled);
 93        super.onSaveInstanceState(savedInstanceState);
 94    }
 95
 96    @Override
 97    public void onNewIntent(Intent intent) {
 98        handleIntent(intent);
 99    }
100
101    private void handleUri(Uri uri) {
102        handleUri(uri, false);
103    }
104
105    private void handleUri(Uri uri, final boolean scanned) {
106        final Intent intent;
107        final XmppUri xmppUri = new XmppUri(uri);
108        final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
109
110        if (SignupUtils.isSupportTokenRegistry() && xmppUri.isValidJid()) {
111            final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
112            final Jid jid = xmppUri.getJid();
113            if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
114                if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
115                    Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_LONG).show();
116                    return;
117                }
118                intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
119                startActivity(intent);
120                return;
121            }
122            if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
123                intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
124                intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
125                startActivity(intent);
126                return;
127            }
128        }
129
130        if (accounts.size() == 0) {
131            if (xmppUri.isValidJid()) {
132                intent = SignupUtils.getSignUpIntent(this);
133                intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
134                startActivity(intent);
135            } else {
136                Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show();
137            }
138
139            return;
140        }
141
142        if (xmppUri.isAction(XmppUri.ACTION_MESSAGE)) {
143
144            final Jid jid = xmppUri.getJid();
145            final String body = xmppUri.getBody();
146
147            if (jid != null) {
148                Class clazz;
149                try {
150                    clazz = Class.forName("eu.siacs.conversations.ui.ShareViaAccountActivity");
151                } catch (ClassNotFoundException e) {
152                    clazz = null;
153
154                }
155                if (clazz != null) {
156                    intent = new Intent(this, clazz);
157                    intent.putExtra("contact", jid.toEscapedString());
158                    intent.putExtra("body", body);
159                } else {
160                    intent = new Intent(this, StartConversationActivity.class);
161                    intent.setAction(Intent.ACTION_VIEW);
162                    intent.setData(uri);
163                    intent.putExtra("account", accounts.get(0).toEscapedString());
164                }
165
166            } else {
167                intent = new Intent(this, ShareWithActivity.class);
168                intent.setAction(Intent.ACTION_SEND);
169                intent.setType("text/plain");
170                intent.putExtra(Intent.EXTRA_TEXT, body);
171            }
172        } else if (accounts.contains(xmppUri.getJid())) {
173            intent = new Intent(getApplicationContext(), EditAccountActivity.class);
174            intent.setAction(Intent.ACTION_VIEW);
175            intent.putExtra("jid", xmppUri.getJid().asBareJid().toString());
176            intent.setData(uri);
177            intent.putExtra("scanned", scanned);
178        } else if (xmppUri.isValidJid()) {
179            intent = new Intent(getApplicationContext(), StartConversationActivity.class);
180            intent.setAction(Intent.ACTION_VIEW);
181            intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
182            intent.putExtra("scanned", scanned);
183            intent.setData(uri);
184        } else {
185            Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show();
186            return;
187        }
188
189        startActivity(intent);
190    }
191
192    private void handleIntent(Intent data) {
193        if (handled) {
194            return;
195        }
196        if (data == null || data.getAction() == null) {
197            finish();
198            return;
199        }
200
201        handled = true;
202
203        switch (data.getAction()) {
204            case Intent.ACTION_VIEW:
205            case Intent.ACTION_SENDTO:
206                handleUri(data.getData());
207                break;
208            case ACTION_SCAN_QR_CODE:
209                Intent intent = new Intent(this, ScanActivity.class);
210                startActivityForResult(intent, REQUEST_SCAN_QR_CODE);
211                return;
212        }
213
214        finish();
215    }
216
217    private boolean allowProvisioning() {
218        final Intent launchIntent = getIntent();
219        return launchIntent != null && launchIntent.getBooleanExtra(EXTRA_ALLOW_PROVISIONING, false);
220    }
221
222    @Override
223    public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
224        super.onActivityResult(requestCode, requestCode, intent);
225        if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
226            final String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
227            if (Strings.isNullOrEmpty(result)) {
228                finish();
229                return;
230            }
231            if (result.startsWith("BEGIN:VCARD\n")) {
232                final Matcher matcher = V_CARD_XMPP_PATTERN.matcher(result);
233                if (matcher.find()) {
234                    handleUri(Uri.parse(matcher.group(2)), true);
235                }
236                finish();
237                return;
238            } else if (QuickConversationsService.isConversations() && looksLikeJsonObject(result) && allowProvisioning()) {
239                ProvisioningUtils.provision(this, result);
240                finish();
241                return;
242            }
243            handleUri(Uri.parse(result), true);
244        }
245        finish();
246    }
247
248    private static boolean looksLikeJsonObject(final String input) {
249        final String trimmed = Strings.emptyToNull(input).trim();
250        return trimmed.charAt(0) == '{' && trimmed.charAt(trimmed.length() - 1) == '}';
251    }
252}