1package eu.siacs.conversations.ui;
2
3import android.Manifest;
4import android.content.ActivityNotFoundException;
5import android.content.Intent;
6import android.content.pm.ActivityInfo;
7import android.net.Uri;
8import android.os.Bundle;
9import android.security.KeyChain;
10import android.security.KeyChainAliasCallback;
11import android.util.Log;
12import android.view.Menu;
13import android.view.MenuItem;
14import android.widget.Toast;
15
16import androidx.annotation.NonNull;
17import androidx.appcompat.app.AppCompatActivity;
18import androidx.databinding.DataBindingUtil;
19
20import java.util.Arrays;
21import java.util.List;
22
23import eu.siacs.conversations.Config;
24import eu.siacs.conversations.R;
25import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
26import eu.siacs.conversations.entities.Account;
27import eu.siacs.conversations.services.XmppConnectionService;
28import eu.siacs.conversations.utils.Compatibility;
29import eu.siacs.conversations.utils.InstallReferrerUtils;
30import eu.siacs.conversations.utils.SignupUtils;
31import eu.siacs.conversations.utils.XmppUri;
32import eu.siacs.conversations.xmpp.Jid;
33
34import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
35import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
36
37import com.google.common.base.Strings;
38
39public class WelcomeActivity extends XmppActivity
40 implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback {
41
42 private static final int REQUEST_IMPORT_BACKUP = 0x63fb;
43
44 private XmppUri inviteUri;
45
46 public static void launch(AppCompatActivity activity) {
47 Intent intent = new Intent(activity, WelcomeActivity.class);
48 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
49 activity.startActivity(intent);
50 activity.overridePendingTransition(0, 0);
51 }
52
53 public void onInstallReferrerDiscovered(final Uri referrer) {
54 Log.d(Config.LOGTAG, "welcome activity: on install referrer discovered " + referrer);
55 if ("xmpp".equalsIgnoreCase(referrer.getScheme())) {
56 final XmppUri xmppUri = new XmppUri(referrer);
57 runOnUiThread(() -> processXmppUri(xmppUri));
58 } else {
59 Log.i(Config.LOGTAG, "install referrer was not an XMPP uri");
60 }
61 }
62
63 private void processXmppUri(final XmppUri xmppUri) {
64 if (!xmppUri.isValidJid()) {
65 return;
66 }
67 final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
68 final Jid jid = xmppUri.getJid();
69 final Intent intent;
70 if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
71 intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
72 } else if (xmppUri.isAction(XmppUri.ACTION_ROSTER)
73 && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
74 intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
75 intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
76 } else {
77 intent = null;
78 }
79 if (intent != null) {
80 startActivity(intent);
81 finish();
82 return;
83 }
84 this.inviteUri = xmppUri;
85 }
86
87 @Override
88 protected void refreshUiReal() {}
89
90 @Override
91 protected void onBackendConnected() {}
92
93 @Override
94 public void onStart() {
95 super.onStart();
96 new InstallReferrerUtils(this);
97 }
98
99 @Override
100 public void onStop() {
101 super.onStop();
102 }
103
104 @Override
105 public void onNewIntent(final Intent intent) {
106 super.onNewIntent(intent);
107 if (intent != null) {
108 setIntent(intent);
109 }
110 }
111
112 @Override
113 protected void onCreate(final Bundle savedInstanceState) {
114 if (getResources().getBoolean(R.bool.portrait_only)) {
115 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
116 }
117 super.onCreate(savedInstanceState);
118 ActivityWelcomeBinding binding =
119 DataBindingUtil.setContentView(this, R.layout.activity_welcome);
120 Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
121 setSupportActionBar(binding.toolbar);
122 configureActionBar(getSupportActionBar(), false);
123 setTitle(null);
124 binding.registerNewAccount.setOnClickListener(
125 v -> {
126 final Intent intent = new Intent(this, PickServerActivity.class);
127 addInviteUri(intent);
128 startActivity(intent);
129 });
130 binding.useExisting.setOnClickListener(
131 v -> {
132 final List<Account> accounts = xmppConnectionService.getAccounts();
133 Intent intent = new Intent(this, EditAccountActivity.class);
134 intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false);
135 if (accounts.size() == 1) {
136 intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
137 intent.putExtra("init", true);
138 } else if (!accounts.isEmpty()) {
139 intent = new Intent(this, ManageAccountActivity.class);
140 }
141 addInviteUri(intent);
142 startActivity(intent);
143 });
144 }
145
146 @Override
147 public boolean onCreateOptionsMenu(final Menu menu) {
148 getMenuInflater().inflate(R.menu.welcome_menu, menu);
149 final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
150 scan.setVisible(Compatibility.hasFeatureCamera(this));
151 return super.onCreateOptionsMenu(menu);
152 }
153
154 @Override
155 public boolean onOptionsItemSelected(final MenuItem item) {
156 switch (item.getItemId()) {
157 case R.id.action_import_backup:
158 if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) {
159 startActivity(new Intent(this, ImportBackupActivity.class));
160 }
161 break;
162 case R.id.action_scan_qr_code:
163 UriHandlerActivity.scan(this, true);
164 break;
165 case R.id.action_add_account_with_cert:
166 addAccountFromKey();
167 break;
168 }
169 return super.onOptionsItemSelected(item);
170 }
171
172 private void addAccountFromKey() {
173 try {
174 KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
175 } catch (final ActivityNotFoundException e) {
176 Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG)
177 .show();
178 }
179 }
180
181 @Override
182 public void alias(final String alias) {
183 if (Strings.isNullOrEmpty(alias)) {
184 runOnUiThread(
185 () ->
186 Toast.makeText(
187 this,
188 R.string.no_certificate_selected,
189 Toast.LENGTH_LONG)
190 .show());
191 return;
192 }
193 xmppConnectionService.createAccountFromKey(alias, this);
194 }
195
196 @Override
197 public void onAccountCreated(final Account account) {
198 final Intent intent = new Intent(this, EditAccountActivity.class);
199 intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
200 intent.putExtra("init", true);
201 addInviteUri(intent);
202 startActivity(intent);
203 }
204
205 @Override
206 public void informUser(final int r) {
207 runOnUiThread(() -> Toast.makeText(this, r, Toast.LENGTH_LONG).show());
208 }
209
210 @Override
211 public void onRequestPermissionsResult(
212 int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
213 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
214 UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
215 if (grantResults.length > 0) {
216 if (allGranted(grantResults)) {
217 switch (requestCode) {
218 case REQUEST_IMPORT_BACKUP:
219 startActivity(new Intent(this, ImportBackupActivity.class));
220 break;
221 }
222 } else if (Arrays.asList(permissions)
223 .contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
224 Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
225 }
226 }
227 if (writeGranted(grantResults, permissions)) {
228 if (xmppConnectionService != null) {
229 xmppConnectionService.restartFileObserver();
230 }
231 }
232 }
233
234 public void addInviteUri(Intent to) {
235 final Intent from = getIntent();
236 if (from != null && from.hasExtra(StartConversationActivity.EXTRA_INVITE_URI)) {
237 final String invite = from.getStringExtra(StartConversationActivity.EXTRA_INVITE_URI);
238 to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, invite);
239 } else if (this.inviteUri != null) {
240 Log.d(Config.LOGTAG, "injecting referrer uri into on-boarding flow");
241 to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, this.inviteUri.toString());
242 }
243 }
244}