1

Is there any way to use a Slider.GracePeriod from the GDK in a high-frequency live card? I have the menu option "send message," and after you tap it and input your message, I'd like to display a "sending message..." alert dialog (or something akin to it) with a grace period slider. However, I haven't had success getting the grace period slider to appear in a dialog (i.e. using the same view that was being passed to Dialog.setContentView() as the argument to Slider.from()), the live card menu activity, or anywhere else. Any ideas?

(This most likely isn't related, but I did notice that Google's GDK reference mentions that a grace period slider animates during the given grace period in "timeInMs." This sounds like an argument to a function or constructor, but if so I couldn't find one. Maybe grace period sliders don't work if you don't set this?)

Note: I am aware of this existing question, but it hasn't been answered yet and it concerns a different kind of both slider and livecard, anyway, so another question seemed appropriate.

tnorth
  • 32
  • 6
  • The slider won't appear unless the view's window has the focus. Can you try overriding `onWindowFocusChanged` in your `Dialog` and starting the grace period there? – Tony Allevato Jan 07 '15 at 19:07
  • It doesn't work properly when I try that - I see the slider appear in the lower left corner of the screen for a split second, and then the dialog is rendered on top of it and I can't see the slider at all (but the success sound does play when it finishes). – tnorth Jan 07 '15 at 20:59

1 Answers1

1

Something like the following should work for you; the following class wraps up grace period/confirmation dialog logic to provide the same experience as built-in Glassware.

public class GracePeriodDialog extends Dialog {
    private static final long COMPLETION_MESSAGE_TIMEOUT_MILLIS = 1000;

    private final DialogInterface.OnClickListener mOnClickListener;
    private final AudioManager mAudioManager;
    private final GestureDetector mGestureDetector;
    private final CharSequence mCompletedText;

    private View mContentView;
    private Slider.GracePeriod mGracePeriod;

    /** Handles the tap gesture to complete the grace period early. */
    private final GestureDetector.BaseListener mBaseListener =
            new GestureDetector.BaseListener() {
        @Override
        public boolean onGesture(Gesture gesture) {
            if (gesture == Gesture.TAP) {
                // Cancel the grace period if the user tapped early so that the completion event
                // doesn't fire twice.
                mGracePeriod.cancel();
                showCompletion();
                return true;
            }
            return false;
        }
    };

    /** Called when the grace period has ended to show the completion message. */
    private final Slider.GracePeriod.Listener mGracePeriodListener =
            new Slider.GracePeriod.Listener() {
        @Override
        public void onGracePeriodEnd() {
            showCompletion();
        }

        @Override
        public void onGracePeriodCancel() {}
    };

    /**
     * Called after a 1-second delay when the grace period has completed (or the user tapped).
     * Forwards the successful completion as an {@code onClick} event on the dialog.
     */
    private final Runnable mDoneRunnable = new Runnable() {
        @Override
        public void run() {
            if (mOnClickListener != null) {
                // Since Glass dialogs do not have buttons, the index passed to onClick is always 0.
                mOnClickListener.onClick(GracePeriodDialog.this, 0);
            }
            dismiss();
        }
    };

    /**
     * Creates a new {@code GracePeriodDialog}. The dialog can show a progress message and icon
     * while the grace period is running, which changes to a completion message when the grace
     * period ends (for example, "Sending" -> "Sent".
     */
    public GracePeriodDialog(Context context,
                       int progressIconId, CharSequence progressText,
                       CharSequence completedText,
                       DialogInterface.OnClickListener onClickListener) {
        super(context);

        mOnClickListener = onClickListener;
        mAudioManager =
                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mGestureDetector =
                new GestureDetector(context).setBaseListener(mBaseListener);
        mCompletedText = completedText;

        updateContentView(progressText, progressIconId);
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        mGracePeriod = Slider.from(mContentView).startGracePeriod(mGracePeriodListener);
    }

    @Override
    public void onBackPressed() {
        mGracePeriod.cancel();
        super.onBackPressed();
    }

    /** Overridden to let the gesture detector handle a possible tap event. */
    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {
        return mGestureDetector.onMotionEvent(event)
                || super.onGenericMotionEvent(event);
    }

    private void showCompletion() {
        mAudioManager.playSoundEffect(Sounds.SUCCESS);
        updateContentView(mCompletedText, R.drawable.ic_done_50);
        mContentView.postDelayed(mDoneRunnable, COMPLETION_MESSAGE_TIMEOUT_MILLIS);
    }

    private void updateContentView(CharSequence text, int iconResId) {
        mContentView = new CardBuilder(getContext(), CardBuilder.Layout.MENU)
                .setText(text)
                .setIcon(iconResId)
                .getView();
        setContentView(mContentView);
    }
}

One possible gotcha to watch out for: make sure that your menu activity isn't being finished (in onOptionsMenuClosed, for example) before you display the dialog or start the grace period.

Tony Allevato
  • 6,429
  • 1
  • 29
  • 34
  • Using this class as-is, the slider still doesn't appear (not even for a split second like it did before), and the progress text doesn't ever change to the completed text unless I tap it. I did confirm that `onAttachedToWindow` is being triggered in `GracePeriodDialog`, and I commented out all calls to `finish` in my menu activity, but to no avail. – tnorth Jan 20 '15 at 23:55
  • I looked at my code again, and the only thing that stands out to me as possibly affecting this is that I'm making the grace period dialog into a system dialog (using the line `mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);`). Hopefully that's ok? I have to do it that way (see [this](http://stackoverflow.com/questions/26238445/how-to-show-alert-dialogs-at-any-time-on-a-high-frequency-live-card) other question I asked earlier). – tnorth Jan 21 '15 at 20:30
  • It's possible that system alerts and the system slider are interacting poorly together. Is there a reason you must show the dialog directly from the service? That can lead to poor UX since nothing prevents it from showing while the service is running but the live card is not visible, and it also requires additional system-level permissions. You should consider showing the dialog from an activity instead (for example, if you have a menu activity from which the user starts the operation using the grace period). – Tony Allevato Jan 22 '15 at 16:16
  • I need to show dialogs in general even when the menu is closed; however, the particular type of dialog that I would be adding a grace period slider to does only need to be shown from the context of a menu. I'll try removing the system alert specification from just the grace period dialog. – tnorth Jan 22 '15 at 19:43
  • Looks like that was the problem. Thanks! – tnorth Jan 22 '15 at 20:24