Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c470d64 authored by Adam Powell's avatar Adam Powell
Browse files

Add voice interaction support to ResolverActivity/ChooserActivity

All options are sent to the VoiceInteractor once ChooserTargetServices
have reported in. We don't perform explicit progressive refinement or
filtering, but an explicit option picked will be invoked.

Also fix a lingering bug around being able to nested-fling the
resolver drawer closed.

Bug 21516866

Change-Id: I6b141f5fa87d74dccec9dcb88110630696e9c38e
parent 43423549
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -100,6 +100,10 @@ public class ChooserActivity extends ResolverActivity {
                    mChooserListAdapter.addServiceResults(sri.originalTarget, sri.resultTargets);
                    unbindService(sri.connection);
                    mServiceConnections.remove(sri.connection);
                    if (mServiceConnections.isEmpty()) {
                        mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
                        sendVoiceChoicesIfNeeded();
                    }
                    break;

                case CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT:
@@ -107,6 +111,7 @@ public class ChooserActivity extends ResolverActivity {
                        Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services");
                    }
                    unbindRemainingServices();
                    sendVoiceChoicesIfNeeded();
                    break;

                default:
@@ -384,6 +389,8 @@ public class ChooserActivity extends ResolverActivity {
                    + WATCHDOG_TIMEOUT_MILLIS + "ms");
            mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
                    WATCHDOG_TIMEOUT_MILLIS);
        } else {
            sendVoiceChoicesIfNeeded();
        }
    }

@@ -418,6 +425,10 @@ public class ChooserActivity extends ResolverActivity {
        mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
    }

    void onSetupVoiceInteraction() {
        // Do nothing. We'll send the voice stuff ourselves.
    }

    void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
        if (mRefinementResultReceiver != null) {
            mRefinementResultReceiver.destroy();
@@ -956,6 +967,10 @@ public class ChooserActivity extends ResolverActivity {
            if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
            unbindService(this);
            mServiceConnections.remove(this);
            if (mServiceConnections.isEmpty()) {
                mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
                sendVoiceChoicesIfNeeded();
            }
        }

        @Override
+88 −5
Original line number Diff line number Diff line
@@ -16,10 +16,17 @@

package com.android.internal.app;

import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.VoiceInteractor;
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
import android.app.VoiceInteractor.Request;
import android.os.AsyncTask;
import android.provider.Settings;
import android.service.chooser.ChooserTarget;
import android.text.TextUtils;
import android.util.Slog;
import android.widget.AbsListView;
@@ -96,6 +103,7 @@ public class ResolverActivity extends Activity {
    private int mProfileSwitchMessageId = -1;
    private final ArrayList<Intent> mIntents = new ArrayList<>();
    private ResolverComparator mResolverComparator;
    private PickTargetOptionRequest mPickOptionRequest;

    private boolean mRegistered;
    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -242,6 +250,9 @@ public class ResolverActivity extends Activity {
                    finish();
                }
            });
            if (isVoiceInteraction()) {
                rdl.setCollapsed(false);
            }
        }

        if (title == null) {
@@ -313,6 +324,39 @@ public class ResolverActivity extends Activity {
            });
            bindProfileView();
        }

        if (isVoiceInteraction()) {
            onSetupVoiceInteraction();
        }
    }

    /**
     * Perform any initialization needed for voice interaction.
     */
    void onSetupVoiceInteraction() {
        // Do it right now. Subclasses may delay this and send it later.
        sendVoiceChoicesIfNeeded();
    }

    void sendVoiceChoicesIfNeeded() {
        if (!isVoiceInteraction()) {
            // Clearly not needed.
            return;
        }


        final Option[] options = new Option[mAdapter.getCount()];
        for (int i = 0, N = options.length; i < N; i++) {
            options[i] = optionForChooserTarget(mAdapter.getItem(i), i);
        }

        mPickOptionRequest = new PickTargetOptionRequest(
                new Prompt(getTitle()), options, null);
        getVoiceInteractor().submitRequest(mPickOptionRequest);
    }

    Option optionForChooserTarget(TargetInfo target, int index) {
        return new Option(target.getDisplayLabel(), index);
    }

    protected final void setAdditionalTargets(Intent[] intents) {
@@ -472,6 +516,14 @@ public class ResolverActivity extends Activity {
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (!isChangingConfigurations() && mPickOptionRequest != null) {
            mPickOptionRequest.cancel();
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
@@ -510,16 +562,12 @@ public class ResolverActivity extends Activity {
        try {
            ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
                    resolveInfo.activityInfo.packageName, 0 /* default flags */);
            return versionNumberAtLeastL(appInfo.targetSdkVersion);
            return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
        } catch (NameNotFoundException e) {
            return false;
        }
    }

    private boolean versionNumberAtLeastL(int versionNumber) {
        return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
    }

    private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
            boolean filtered) {
        boolean enabled = false;
@@ -1644,4 +1692,39 @@ public class ResolverActivity extends Activity {
                && match <= IntentFilter.MATCH_CATEGORY_PATH;
    }

    static class PickTargetOptionRequest extends PickOptionRequest {
        public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
                @Nullable Bundle extras) {
            super(prompt, options, extras);
        }

        @Override
        public void onCancel() {
            super.onCancel();
            final ResolverActivity ra = (ResolverActivity) getActivity();
            if (ra != null) {
                ra.mPickOptionRequest = null;
                ra.finish();
            }
        }

        @Override
        public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
            super.onPickOptionResult(finished, selections, result);
            if (selections.length != 1) {
                // TODO In a better world we would filter the UI presented here and let the
                // user refine. Maybe later.
                return;
            }

            final ResolverActivity ra = (ResolverActivity) getActivity();
            if (ra != null) {
                final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex());
                if (ra.onTargetSelected(ti, false)) {
                    ra.mPickOptionRequest = null;
                    ra.finish();
                }
            }
        }
    }
}
+15 −1
Original line number Diff line number Diff line
@@ -144,6 +144,14 @@ public class ResolverDrawerLayout extends ViewGroup {
        return mCollapseOffset > 0;
    }

    public void setCollapsed(boolean collapsed) {
        if (!isLaidOut()) {
            mOpenOnLayout = collapsed;
        } else {
            smoothScrollTo(collapsed ? mCollapsibleHeight : 0, 0);
        }
    }

    private boolean isMoving() {
        return mIsDragging || !mScroller.isFinished();
    }
@@ -575,7 +583,13 @@ public class ResolverDrawerLayout extends ViewGroup {
    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        if (!consumed && Math.abs(velocityY) > mMinFlingVelocity) {
            if (mOnDismissedListener != null
                    && velocityY < 0 && mCollapseOffset > mCollapsibleHeight) {
                smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, velocityY);
                mDismissOnScrollerFinished = true;
            } else {
                smoothScrollTo(velocityY > 0 ? 0 : mCollapsibleHeight, velocityY);
            }
            return true;
        }
        return false;
+1 −0
Original line number Diff line number Diff line
@@ -2457,6 +2457,7 @@
            <intent-filter>
                <action android:name="android.intent.action.CHOOSER" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.VOICE" />
            </intent-filter>
        </activity>
        <activity android:name="com.android.internal.app.IntentForwarderActivity"