13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
2fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert * Copyright (C) 2010 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1940736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwoodimport com.google.common.annotations.VisibleForTesting;
2040736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood
2181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringertimport android.app.SearchManager;
2281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringertimport android.content.Intent;
2381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringertimport android.net.Uri;
2481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringertimport android.os.Bundle;
2581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
2793bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert * Some utilities for suggestions.
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
2993bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringertpublic class SuggestionUtils {
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3193bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    private SuggestionUtils() {
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3493bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert    public static Intent getSuggestionIntent(SuggestionCursor suggestion, Bundle appSearchData) {
3593bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        String action = suggestion.getSuggestionIntentAction();
3681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
3793bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        String data = suggestion.getSuggestionIntentDataString();
3893bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        String query = suggestion.getSuggestionQuery();
3993bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        String userQuery = suggestion.getUserQuery();
4093bd2e70b8b08da1ec37fd0e990dac05551d2e90Bjorn Bringert        String extraData = suggestion.getSuggestionIntentExtraData();
4181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
4281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        // Now build the Intent
4381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        Intent intent = new Intent(action);
4481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        // We need CLEAR_TOP to avoid reusing an old task that has other activities
4681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        // on top of the one we want.
4781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
4881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (data != null) {
4981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            intent.setData(Uri.parse(data));
5081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
5181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        intent.putExtra(SearchManager.USER_QUERY, userQuery);
5281a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (query != null) {
5381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            intent.putExtra(SearchManager.QUERY, query);
5481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
5581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (extraData != null) {
5681a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
5781a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
5881a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        if (appSearchData != null) {
5981a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert            intent.putExtra(SearchManager.APP_DATA, appSearchData);
6081a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        }
6181a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
6238eb02e676db9e5a633e3c88a90beb8a477b1ca1Bjorn Bringert        intent.setComponent(suggestion.getSuggestionIntentComponent());
6381a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert        return intent;
6481a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert    }
6581a0897ff9685f3313c58294bf7973700c468b2bBjorn Bringert
66f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    /**
67f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood     * Gets a unique key that identifies a suggestion. This is used to avoid
68f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood     * duplicate suggestions.
69f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood     */
70f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    public static String getSuggestionKey(Suggestion suggestion) {
71f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        String action = makeKeyComponent(suggestion.getSuggestionIntentAction());
72f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        String data = makeKeyComponent(normalizeUrl(suggestion.getSuggestionIntentDataString()));
73f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        String query = makeKeyComponent(normalizeUrl(suggestion.getSuggestionQuery()));
74f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        // calculating accurate size of string builder avoids an allocation vs starting with
75f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        // the default size and having to expand.
76f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        int size = action.length() + 2 + data.length() + query.length();
77f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        return new StringBuilder(size)
78f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .append(action)
79f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .append('#')
80f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .append(data)
81f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .append('#')
82f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .append(query)
83f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                .toString();
84f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    }
85f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood
86f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    private static String makeKeyComponent(String str) {
87f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        return str == null ? "" : str;
88f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    }
89f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood
9040736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood    private static final String SCHEME_SEPARATOR = "://";
9140736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood    private static final String DEFAULT_SCHEME = "http";
9240736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood
9340736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood    /**
9440736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood     * Simple url normalization that adds http:// if no scheme exists, and
9540736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood     * strips empty paths, e.g.,
9640736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood     * www.google.com/ -> http://www.google.com.  Used to prevent obvious
97f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood     * duplication of nav suggestions, bookmarks and urls entered by the user.
98f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood     */
9940736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood    @VisibleForTesting
10040736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood    static String normalizeUrl(String url) {
10140736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood        String normalized;
10240736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood        if (url != null) {
10340736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            int start;
10440736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            int schemePos = url.indexOf(SCHEME_SEPARATOR);
10540736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            if (schemePos == -1) {
10640736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood                // no scheme - add the default
10740736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood                normalized = DEFAULT_SCHEME + SCHEME_SEPARATOR + url;
10840736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood                start = DEFAULT_SCHEME.length() + SCHEME_SEPARATOR.length();
10940736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            } else {
11040736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood                normalized = url;
11140736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood                start = schemePos + SCHEME_SEPARATOR.length();
11240736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            }
11340736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            int end = normalized.length();
11440736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            if (normalized.indexOf('/', start) == end - 1) {
115f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood                end--;
116f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood            }
11740736d6090ddc45af9c44a436dfcdaef93e5d025Mathew Inwood            return normalized.substring(0, end);
118f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        }
119f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood        return url;
120f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood    }
121f32ccb142919068f22ac0a0c19459c9153f70ecaMathew Inwood
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
123