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}