diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js index 4844b9ee0..66cd71e2e 100644 --- a/js/gdm/authPrompt.js +++ b/js/gdm/authPrompt.js @@ -4,6 +4,7 @@ const { Clutter, GLib, GObject, Pango, Shell, St } = imports.gi; const Animation = imports.ui.animation; +const AuthList = imports.gdm.authList; const Batch = imports.gdm.batch; const GdmUtil = imports.gdm.util; const OVirt = imports.gdm.oVirt; @@ -73,6 +74,7 @@ var AuthPrompt = GObject.registerClass({ this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this)); this._userVerifier.connect('show-message', this._onShowMessage.bind(this)); + this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this)); this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this)); this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this)); this._userVerifier.connect('reset', this._onReset.bind(this)); @@ -90,7 +92,7 @@ var AuthPrompt = GObject.registerClass({ this._hasCancelButton = this._mode === AuthPromptMode.UNLOCK_OR_LOG_IN; - this._initEntryRow(); + this._initInputRow(); let capsLockPlaceholder = new St.Label(); this.add_child(capsLockPlaceholder); @@ -129,7 +131,7 @@ var AuthPrompt = GObject.registerClass({ return super.vfunc_key_press_event(keyPressEvent); } - _initEntryRow() { + _initInputRow() { this._mainBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', vertical: false, @@ -152,6 +154,25 @@ var AuthPrompt = GObject.registerClass({ this.cancelButton.opacity = 0; this._mainBox.add_child(this.cancelButton); + this._authList = new AuthList.AuthList(); + this._authList.set({ + visible: false, + }); + this._authList.connect('activate', (list, key) => { + this._authList.reactive = false; + this._authList.ease({ + opacity: 0, + duration: MESSAGE_FADE_OUT_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this._authList.clear(); + this._authList.hide(); + this._userVerifier.selectChoice(this._queryingService, key); + }, + }); + }); + this._mainBox.add_child(this._authList); + let entryParams = { style_class: 'login-dialog-prompt-entry', can_focus: true, @@ -290,6 +311,20 @@ var AuthPrompt = GObject.registerClass({ this.emit('prompted'); } + _onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) { + if (this._queryingService) + this.clear(); + + this._queryingService = serviceName; + + if (this._preemptiveAnswer) + this._preemptiveAnswer = null; + + this.setChoiceList(promptMessage, choiceList); + this.updateSensitivity(true); + this.emit('prompted'); + } + _onCredentialManagerAuthenticated() { if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) this.reset(); @@ -425,15 +460,46 @@ var AuthPrompt = GObject.registerClass({ clear() { this._entry.text = ''; this.stopSpinning(); + this._authList.clear(); + this._authList.hide(); } setQuestion(question) { this._entry.hint_text = question; + this._authList.hide(); this._entry.show(); this._entry.grab_key_focus(); } + _fadeInChoiceList() { + this._authList.set({ + opacity: 0, + visible: true, + reactive: false, + }); + this._authList.ease({ + opacity: 255, + duration: MESSAGE_FADE_OUT_ANIMATION_TIME, + transition: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => (this._authList.reactive = true), + }); + } + + setChoiceList(promptMessage, choiceList) { + this._authList.clear(); + this._authList.label.text = promptMessage; + for (let key in choiceList) { + let text = choiceList[key]; + this._authList.addItem(key, text); + } + + this._entry.hide(); + if (this._message.text === '') + this._message.hide(); + this._fadeInChoiceList(); + } + getAnswer() { let text; @@ -469,6 +535,7 @@ var AuthPrompt = GObject.registerClass({ else this._message.remove_style_class_name('login-dialog-message-hint'); + this._message.show(); if (message) { this._message.remove_all_transitions(); this._message.text = message; diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js index 760d7e9ef..d48ea5416 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -418,6 +418,11 @@ var LoginDialog = GObject.registerClass({ this._userManager = AccountsService.UserManager.get_default(); this._gdmClient = new Gdm.Client(); + try { + this._gdmClient.set_enabled_extensions([Gdm.UserVerifierChoiceList.interface_info().name]); + } catch (e) { + } + this._settings = new Gio.Settings({ schema_id: GdmUtil.LOGIN_SCREEN_SCHEMA }); this._settings.connect('changed::%s'.format(GdmUtil.BANNER_MESSAGE_KEY), diff --git a/js/gdm/util.js b/js/gdm/util.js index 72561daab..b5f31a2ff 100644 --- a/js/gdm/util.js +++ b/js/gdm/util.js @@ -237,6 +237,10 @@ var ShellUserVerifier = class { this._disconnectSignals(); this._userVerifier.run_dispose(); this._userVerifier = null; + if (this._userVerifierChoiceList) { + this._userVerifierChoiceList.run_dispose(); + this._userVerifierChoiceList = null; + } } } @@ -267,6 +271,10 @@ var ShellUserVerifier = class { } } + selectChoice(serviceName, key) { + this._userVerifierChoiceList.call_select_choice(serviceName, key, this._cancellable, null); + } + answerQuery(serviceName, answer) { if (!this.hasPendingMessages) { this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); @@ -453,6 +461,11 @@ var ShellUserVerifier = class { return; } + if (this._client.get_user_verifier_choice_list) + this._userVerifierChoiceList = this._client.get_user_verifier_choice_list(); + else + this._userVerifierChoiceList = null; + this.reauthenticating = true; this._connectSignals(); this._beginVerification(); @@ -471,6 +484,11 @@ var ShellUserVerifier = class { return; } + if (this._client.get_user_verifier_choice_list) + this._userVerifierChoiceList = this._client.get_user_verifier_choice_list(); + else + this._userVerifierChoiceList = null; + this._connectSignals(); this._beginVerification(); this._hold.release(); @@ -496,6 +514,9 @@ var ShellUserVerifier = class { this._signalIds.push(id); id = this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this)); this._signalIds.push(id); + + if (this._userVerifierChoiceList) + this._userVerifierChoiceList.connect('choice-query', this._onChoiceListQuery.bind(this)); } _disconnectSignals() { @@ -576,6 +597,13 @@ var ShellUserVerifier = class { this._startService(FINGERPRINT_SERVICE_NAME); } + _onChoiceListQuery(client, serviceName, promptMessage, list) { + if (!this.serviceIsForeground(serviceName)) + return; + + this.emit('show-choice-list', serviceName, promptMessage, list.deep_unpack()); + } + _onInfo(client, serviceName, info) { if (this.serviceIsForeground(serviceName)) { this._queueMessage(serviceName, info, MessageType.INFO); diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js index 367117bfd..b33da66a1 100644 --- a/js/ui/unlockDialog.js +++ b/js/ui/unlockDialog.js @@ -493,6 +493,13 @@ var UnlockDialog = GObject.registerClass({ this._gdmClient = new Gdm.Client(); + try { + this._gdmClient.set_enabled_extensions([ + Gdm.UserVerifierChoiceList.interface_info().name, + ]); + } catch (e) { + } + this._adjustment = new St.Adjustment({ actor: this, lower: 0,