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