WelcomeActivity.java

  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.security.SecureRandom;
 21import java.util.Arrays;
 22import java.util.List;
 23import java.util.HashSet;
 24import java.util.UUID;
 25
 26import eu.siacs.conversations.Config;
 27import eu.siacs.conversations.R;
 28import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
 29import eu.siacs.conversations.entities.Account;
 30import eu.siacs.conversations.services.XmppConnectionService;
 31import eu.siacs.conversations.utils.CryptoHelper;
 32import eu.siacs.conversations.utils.Compatibility;
 33import eu.siacs.conversations.utils.InstallReferrerUtils;
 34import eu.siacs.conversations.utils.SignupUtils;
 35import eu.siacs.conversations.utils.XmppUri;
 36import eu.siacs.conversations.xmpp.Jid;
 37
 38import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
 39import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
 40
 41public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, XmppConnectionService.OnAccountUpdate, KeyChainAliasCallback {
 42
 43    private static final int REQUEST_IMPORT_BACKUP = 0x63fb;
 44
 45    private XmppUri inviteUri;
 46    private Account onboardingAccount = null;
 47
 48    public static void launch(AppCompatActivity activity) {
 49        Intent intent = new Intent(activity, WelcomeActivity.class);
 50        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 51        activity.startActivity(intent);
 52        activity.overridePendingTransition(0, 0);
 53    }
 54
 55    public void onInstallReferrerDiscovered(final Uri referrer) {
 56        Log.d(Config.LOGTAG, "welcome activity: on install referrer discovered " + referrer);
 57        if ("xmpp".equalsIgnoreCase(referrer.getScheme())) {
 58            final XmppUri xmppUri = new XmppUri(referrer);
 59            runOnUiThread(() -> processXmppUri(xmppUri));
 60        } else {
 61            Log.i(Config.LOGTAG, "install referrer was not an XMPP uri");
 62        }
 63    }
 64
 65    private void processXmppUri(final XmppUri xmppUri) {
 66        if (!xmppUri.isValidJid()) {
 67            return;
 68        }
 69        final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
 70        final Jid jid = xmppUri.getJid();
 71        final Intent intent;
 72        if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
 73            intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
 74        } else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
 75            intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
 76            intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
 77        } else {
 78            intent = null;
 79        }
 80        if (intent != null) {
 81            startActivity(intent);
 82            finish();
 83            return;
 84        }
 85        this.inviteUri = xmppUri;
 86    }
 87
 88    @Override
 89    protected synchronized void refreshUiReal() {
 90        if (onboardingAccount == null) return;
 91        if (onboardingAccount.getStatus() != Account.State.ONLINE) return;
 92
 93        Intent intent = new Intent(this, StartConversationActivity.class);
 94        intent.putExtra("init", true);
 95        intent.putExtra(EXTRA_ACCOUNT, onboardingAccount.getJid().asBareJid().toEscapedString());
 96        onboardingAccount = null;
 97        startActivity(intent);
 98        finish();
 99    }
100
101    @Override
102    public void onAccountUpdate() {
103        refreshUi();
104    }
105
106    @Override
107    void onBackendConnected() {
108
109    }
110
111    @Override
112    public void onStart() {
113        super.onStart();
114        final int theme = findTheme();
115        if (this.mTheme != theme) {
116            recreate();
117        }
118        new InstallReferrerUtils(this);
119    }
120
121    @Override
122    public void onStop() {
123        super.onStop();
124    }
125
126    @Override
127    public void onNewIntent(Intent intent) {
128        if (intent != null) {
129            setIntent(intent);
130        }
131    }
132
133    @Override
134    protected void onCreate(final Bundle savedInstanceState) {
135        if (getResources().getBoolean(R.bool.portrait_only)) {
136            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
137        }
138        super.onCreate(savedInstanceState);
139        getPreferences().edit().putStringSet("pstn_gateways", new HashSet<>()).apply();
140        ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_welcome);
141        setSupportActionBar(binding.toolbar);
142        configureActionBar(getSupportActionBar(), false);
143        binding.registerNewAccount.setOnClickListener(v -> {
144            if (hasInviteUri()) {
145                final Intent intent = new Intent(this, MagicCreateActivity.class);
146                addInviteUri(intent);
147                startActivity(intent);
148            } else {
149                binding.registerNewAccount.setText("Working...");
150                binding.registerNewAccount.setEnabled(false);
151                onboardingAccount = new Account(Jid.ofLocalAndDomain(UUID.randomUUID().toString(), Config.ONBOARDING_DOMAIN.toEscapedString()), CryptoHelper.createPassword(new SecureRandom()));
152                onboardingAccount.setOption(Account.OPTION_REGISTER, true);
153                onboardingAccount.setOption(Account.OPTION_FIXED_USERNAME, true);
154                xmppConnectionService.createAccount(onboardingAccount);
155            }
156        });
157        binding.useExisting.setOnClickListener(v -> {
158            final List<Account> accounts = xmppConnectionService.getAccounts();
159            Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class);
160            intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false);
161            if (accounts.size() == 1) {
162                intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
163                intent.putExtra("init", true);
164            } else if (accounts.size() >= 1) {
165                intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class);
166            }
167            addInviteUri(intent);
168            startActivity(intent);
169        });
170        binding.useSnikket.setOnClickListener(v -> {
171            final List<Account> accounts = xmppConnectionService.getAccounts();
172            Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class);
173            intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false);
174            intent.putExtra("snikket", true);
175            if (accounts.size() == 1) {
176                intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
177                intent.putExtra("init", true);
178            } else if (accounts.size() >= 1) {
179                intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class);
180            }
181            addInviteUri(intent);
182            startActivity(intent);
183        });
184    }
185
186    @Override
187    public boolean onCreateOptionsMenu(Menu menu) {
188        getMenuInflater().inflate(R.menu.welcome_menu, menu);
189        final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
190        scan.setVisible(Compatibility.hasFeatureCamera(this));
191        return super.onCreateOptionsMenu(menu);
192    }
193
194
195
196    @Override
197    public boolean onOptionsItemSelected(MenuItem item) {
198        switch (item.getItemId()) {
199            case R.id.action_import_backup:
200                if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) {
201                    startActivity(new Intent(this, ImportBackupActivity.class));
202                }
203                break;
204            case R.id.action_scan_qr_code:
205                UriHandlerActivity.scan(this, true);
206                break;
207            case R.id.action_add_account_with_cert:
208                addAccountFromKey();
209                break;
210        }
211        return super.onOptionsItemSelected(item);
212    }
213
214    private void addAccountFromKey() {
215        try {
216            KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
217        } catch (ActivityNotFoundException e) {
218            Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
219        }
220    }
221
222    @Override
223    public void alias(final String alias) {
224        if (alias != null) {
225            xmppConnectionService.createAccountFromKey(alias, this);
226        }
227    }
228
229    @Override
230    public void onAccountCreated(final Account account) {
231        final Intent intent = new Intent(this, EditAccountActivity.class);
232        intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
233        intent.putExtra("init", true);
234        addInviteUri(intent);
235        startActivity(intent);
236    }
237
238    @Override
239    public void informUser(final int r) {
240        runOnUiThread(() -> Toast.makeText(this, r, Toast.LENGTH_LONG).show());
241    }
242
243    @Override
244    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
245        UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
246        if (grantResults.length > 0) {
247            if (allGranted(grantResults)) {
248                switch (requestCode) {
249                    case REQUEST_IMPORT_BACKUP:
250                        startActivity(new Intent(this, ImportBackupActivity.class));
251                        break;
252                }
253            } else if (Arrays.asList(permissions).contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
254                Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
255            }
256        }
257        if (writeGranted(grantResults, permissions)) {
258            if (xmppConnectionService != null) {
259                xmppConnectionService.restartFileObserver();
260            }
261        }
262    }
263
264    protected boolean hasInviteUri() {
265        final Intent from = getIntent();
266        if (from != null && from.hasExtra(StartConversationActivity.EXTRA_INVITE_URI)) return true;
267        return this.inviteUri != null;
268    }
269
270    public void addInviteUri(Intent to) {
271        final Intent from = getIntent();
272        if (from != null && from.hasExtra(StartConversationActivity.EXTRA_INVITE_URI)) {
273            final String invite = from.getStringExtra(StartConversationActivity.EXTRA_INVITE_URI);
274            to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, invite);
275        } else if (this.inviteUri != null) {
276            Log.d(Config.LOGTAG, "injecting referrer uri into on-boarding flow");
277            to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, this.inviteUri.toString());
278        }
279    }
280
281}