Add first inline switch result.

Adds gestures as Inline switch results to Settings Search.

Change-Id: Ie9b69ac2a54756d5b756401e348cae0a02c907c9
Fixes: 33669037
Test: make RunSettingsRoboTests
This commit is contained in:
Matthew Fritze
2016-12-15 13:34:29 -08:00
parent d65ab0e63f
commit 65fb01b520
31 changed files with 1281 additions and 330 deletions

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="60dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="48dp"
android:maxHeight="48dp"/>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLength="60"
android:maxLines="10"/>
</RelativeLayout>
<Switch
android:id="@+id/switchView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="end|center_vertical"
android:paddingStart="16dp"
android:orientation="vertical"/>
</LinearLayout>

View File

@@ -14,50 +14,59 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingEnd="?android:attr/scrollbarSize"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/search_result_item_image_size"
android:layout_height="@dimen/search_result_item_image_size"
android:layout_marginStart="@dimen/search_result_item_image_margin_start"
android:layout_marginEnd="@dimen/search_result_item_image_margin_end"
android:scaleType="centerInside"/>
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/icon_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:orientation="vertical">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="@+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
android:minWidth="60dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="48dp"
android:maxHeight="48dp"/>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -20,7 +20,10 @@ import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.util.ArrayMap;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class DoubleTapPowerPreferenceController extends GesturePreferenceController {
@@ -61,4 +64,14 @@ public class DoubleTapPowerPreferenceController extends GesturePreferenceControl
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0);
return cameraDisabled == 0;
}
@Override
public ResultPayload getResultPayload() {
ArrayMap<Integer, Boolean> valueMap = new ArrayMap<>();
valueMap.put(0, true);
valueMap.put(1, false);
return new InlineSwitchPayload(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
ResultPayload.SettingsSource.SECURE, valueMap);
}
}

View File

@@ -21,8 +21,11 @@ import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.util.ArrayMap;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class DoubleTapScreenPreferenceController extends GesturePreferenceController {
@@ -67,4 +70,14 @@ public class DoubleTapScreenPreferenceController extends GesturePreferenceContro
protected boolean isSwitchPrefEnabled() {
return mAmbientConfig.pulseOnDoubleTapEnabled(mUserId);
}
@Override
public ResultPayload getResultPayload() {
ArrayMap<Integer, Boolean> valueMap = new ArrayMap<>();
valueMap.put(1, true);
valueMap.put(0, false);
return new InlineSwitchPayload(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
ResultPayload.SettingsSource.SECURE, valueMap);
}
}

View File

@@ -24,8 +24,11 @@ import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class DoubleTwistPreferenceController extends GesturePreferenceController {
@@ -82,4 +85,14 @@ public class DoubleTwistPreferenceController extends GesturePreferenceController
}
return false;
}
@Override
public ResultPayload getResultPayload() {
ArrayMap<Integer, Boolean> valueMap = new ArrayMap<>();
valueMap.put(1, true);
valueMap.put(0, false);
return new InlineSwitchPayload(Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
ResultPayload.SettingsSource.SECURE, valueMap);
}
}

View File

@@ -167,21 +167,32 @@ public class GestureSettings extends DashboardFragment {
@Override
public List<String> getNonIndexableKeys(Context context) {
ArrayList<String> result = new ArrayList<String>();
List<PreferenceController> preferenceControllers =
getPreferenceControllers(context);
for(PreferenceController controller : preferenceControllers) {
controller.updateNonIndexableKeys(result);
}
return result;
}
@Override
public List<PreferenceController> getPreferenceControllers(Context context) {
List<PreferenceController> controllers = new ArrayList<>();
AmbientDisplayConfiguration ambientConfig
= new AmbientDisplayConfiguration(context);
new DoubleTapPowerPreferenceController(context, null /* lifecycle */)
.updateNonIndexableKeys(result);
new PickupGesturePreferenceController(
context, null /* lifecycle */, ambientConfig, UserHandle.myUserId())
.updateNonIndexableKeys(result);
new DoubleTapScreenPreferenceController(
context, null /* lifecycle */, ambientConfig, UserHandle.myUserId())
.updateNonIndexableKeys(result);
new SwipeToNotificationPreferenceController(context, null /* lifecycle */)
.updateNonIndexableKeys(result);
new DoubleTwistPreferenceController(context, null /* lifecycle */)
.updateNonIndexableKeys(result);
return result;
controllers.add(new DoubleTapPowerPreferenceController(context,
null /* lifecycle */));
controllers.add(new PickupGesturePreferenceController(context,
null /* lifecycle */, ambientConfig, UserHandle.myUserId()));
controllers.add(new DoubleTapScreenPreferenceController(context,
null /* lifecycle */, ambientConfig, UserHandle.myUserId()));
controllers.add(new SwipeToNotificationPreferenceController(context,
null /* lifecycle */));
controllers.add(new DoubleTwistPreferenceController(context,
null /* lifecycle */));
return controllers;
}
};
}

View File

@@ -21,8 +21,11 @@ import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.util.ArrayMap;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class PickupGesturePreferenceController extends GesturePreferenceController {
@@ -68,4 +71,13 @@ public class PickupGesturePreferenceController extends GesturePreferenceControll
return true;
}
@Override
public ResultPayload getResultPayload() {
ArrayMap<Integer, Boolean> valueMap = new ArrayMap<>();
valueMap.put(1, true);
valueMap.put(0, false);
return new InlineSwitchPayload(Settings.Secure.DOZE_PULSE_ON_PICK_UP,
ResultPayload.SettingsSource.SECURE, valueMap);
}
}

View File

@@ -20,7 +20,10 @@ import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.util.ArrayMap;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class SwipeToNotificationPreferenceController extends GesturePreferenceController {
@@ -60,4 +63,14 @@ public class SwipeToNotificationPreferenceController extends GesturePreferenceCo
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0)
== 1;
}
@Override
public ResultPayload getResultPayload() {
ArrayMap<Integer, Boolean> valueMap = new ArrayMap<>();
valueMap.put(1, true);
valueMap.put(0, false);
return new InlineSwitchPayload(Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
ResultPayload.SettingsSource.SECURE, valueMap);
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search2;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.BadParcelableException;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.search.Index;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_RANK;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_ICON;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_KEY;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
import static com.android.settings.search2.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD;
/**
* Controller to Build search results from {@link Cursor} Objects.
*
* Each converted {@link Cursor} has the following fields:
* - String Title
* - String Summary
* - int rank
* - {@link Drawable} icon
* - {@link ResultPayload} payload
*/
class CursorToSearchResultConverter {
private final String TAG = "CursorConverter";
private final Context mContext;
public CursorToSearchResultConverter(Context context) {
mContext = context;
}
public List<SearchResult> convertCursor(Cursor cursorResults) {
if (cursorResults == null) {
return null;
}
final Map<String, Context> contextMap = new HashMap<>();
final ArrayList<SearchResult> results = new ArrayList<>();
while (cursorResults.moveToNext()) {
SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults);
if (result != null) {
results.add(result);
}
}
Collections.sort(results);
return results;
}
private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
Cursor cursor) {
final String pkgName = cursor.getString(COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
final String action = cursor.getString(COLUMN_INDEX_INTENT_ACTION);
final String title = cursor.getString(COLUMN_INDEX_TITLE);
final String summaryOn = cursor.getString(COLUMN_INDEX_SUMMARY_ON);
final String className = cursor.getString(COLUMN_INDEX_CLASS_NAME);
final int rank = cursor.getInt(COLUMN_INDEX_RANK);
final String key = cursor.getString(COLUMN_INDEX_KEY);
final String iconResStr = cursor.getString(COLUMN_INDEX_ICON);
final int payloadType = cursor.getInt(COLUMN_INDEX_PAYLOAD_TYPE);
final byte[] marshalledPayload = cursor.getBlob(COLUMN_INDEX_PAYLOAD);
final ResultPayload payload;
if (marshalledPayload != null) {
payload = getUnmarshalledPayload(marshalledPayload, payloadType);
} else if (payloadType == ResultPayload.PayloadType.INTENT) {
payload = getIntentPayload(cursor, action, key, className, pkgName);
} else {
Log.w(TAG, "Error creating payload - bad marshalling data or mismatched types");
return null;
}
final SearchResult.Builder builder = new SearchResult.Builder();
builder.addTitle(title)
.addSummary(summaryOn)
.addRank(rank)
.addIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
.addPayload(payload);
return builder.build();
}
private Drawable getIconForPackage(Map<String, Context> contextMap, String pkgName,
String className, String iconResStr) {
final int iconId = TextUtils.isEmpty(iconResStr)
? 0 : Integer.parseInt(iconResStr);
Drawable icon;
Context packageContext;
if (iconId == 0) {
icon = null;
} else {
if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(pkgName)) {
packageContext = contextMap.get(pkgName);
if (packageContext == null) {
try {
packageContext = mContext.createPackageContext(pkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot create Context for package: " + pkgName);
return null;
}
contextMap.put(pkgName, packageContext);
}
} else {
packageContext = mContext;
}
try {
icon = packageContext.getDrawable(iconId);
} catch (Resources.NotFoundException nfe) {
icon = null;
}
}
return icon;
}
private IntentPayload getIntentPayload(Cursor cursor, String action, String key,
String className, String pkgName ) {
IntentPayload payload;
if (TextUtils.isEmpty(action)) {
final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
// Action is null, we will launch it as a sub-setting
final Bundle args = new Bundle();
args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
final Intent intent = Utils.onBuildStartFragmentIntent(mContext,
className, args, null, 0, screenTitle, false);
payload = new IntentPayload(intent);
} else {
final Intent intent = new Intent(action);
final String targetClass = cursor.getString(
Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
if (!TextUtils.isEmpty(pkgName) && !TextUtils.isEmpty(targetClass)) {
final ComponentName component = new ComponentName(pkgName, targetClass);
intent.setComponent(component);
}
intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
payload = new IntentPayload(intent);
}
return payload;
}
private ResultPayload getUnmarshalledPayload(byte[] unmarshalledPayload, int payloadType) {
try {
switch (payloadType) {
case ResultPayload.PayloadType.INLINE_SWITCH:
return ResultPayloadUtils.unmarshall(unmarshalledPayload,
InlineSwitchPayload.CREATOR);
}
} catch (BadParcelableException e) {
Log.w(TAG, "Error creating parcelable: " + e);
}
return null;
}
}

View File

@@ -66,6 +66,10 @@ public class DatabaseIndexingUtils {
*/
public static Map<String, PreferenceController> getPreferenceControllerUriMap(
String className, Context context) {
if (context == null) {
return null;
}
final Class<?> clazz = getIndexableClass(className);
if (clazz == null) {
@@ -77,6 +81,7 @@ public class DatabaseIndexingUtils {
// Will be non null only for a Local provider implementing a
// SEARCH_INDEX_DATA_PROVIDER field
final Indexable.SearchIndexProvider provider = getSearchIndexProvider(clazz);
List<PreferenceController> controllers =
provider.getPreferenceControllers(context);
@@ -94,8 +99,9 @@ public class DatabaseIndexingUtils {
}
/**
* @param uriMap Map between the {@link PreferenceController} keys and the controllers themselves.
* @param key The look up key
* @param uriMap Map between the {@link PreferenceController} keys
* and the controllers themselves.
* @param key The look-up key
* @return The Payload from the {@link PreferenceController} specified by the key, if it exists.
* Otherwise null.
*/
@@ -135,15 +141,13 @@ public class DatabaseIndexingUtils {
} catch (NoSuchFieldException e) {
Log.d(TAG, "Cannot find field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
} catch (SecurityException se) {
Log.d(TAG,
"Security exception for field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
Log.d(TAG, "Security exception for field '" +
FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
} catch (IllegalAccessException e) {
Log.d(TAG,
"Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
Log.d(TAG, "Illegal access to field '" + FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
} catch (IllegalArgumentException e) {
Log.d(TAG,
"Illegal argument when accessing field '" +
FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
Log.d(TAG, "Illegal argument when accessing field '" +
FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "'");
}
return null;
}

View File

@@ -48,14 +48,36 @@ import java.util.Map;
public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
private static final String LOG = "DatabaseResultLoader";
private final String mQueryText;
private final Context mContext;
protected final SQLiteDatabase mDatabase;
private final CursorToSearchResultConverter mConverter;
/* These indices are used to match the columns of the this loader's SELECT statement.
These are not necessarily the same order or coverage as the schema defined in
IndexDatabaseHelper */
public static final int COLUMN_INDEX_RANK = 0;
public static final int COLUMN_INDEX_TITLE = 1;
public static final int COLUMN_INDEX_SUMMARY_ON = 2;
public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
public static final int COLUMN_INDEX_ENTRIES = 4;
public static final int COLUMN_INDEX_KEYWORDS = 5;
public static final int COLUMN_INDEX_CLASS_NAME = 6;
public static final int COLUMN_INDEX_SCREEN_TITLE = 7;
public static final int COLUMN_INDEX_ICON = 8;
public static final int COLUMN_INDEX_INTENT_ACTION = 9;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 10;
public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 11;
public static final int COLUMN_INDEX_ENABLED = 12;
public static final int COLUMN_INDEX_KEY = 13;
public static final int COLUMN_INDEX_PAYLOAD_TYPE = 14;
public static final int COLUMN_INDEX_PAYLOAD = 15;
public DatabaseResultLoader(Context context, String queryText) {
super(context);
mDatabase = IndexDatabaseHelper.getInstance(context).getReadableDatabase();
mQueryText = queryText;
mContext = context;
mConverter = new CursorToSearchResultConverter(context);
}
@Override
@@ -72,7 +94,7 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
String query = getSQLQuery();
Cursor result = mDatabase.rawQuery(query, null);
return parseCursorForSearch(result);
return mConverter.convertCursor(result);
}
@Override
@@ -86,100 +108,12 @@ public class DatabaseResultLoader extends AsyncLoader<List<SearchResult>> {
"data_summary_off, data_entries, data_keywords, class_name, screen_title,"
+ " icon, " +
"intent_action, intent_target_package, intent_target_class, enabled, " +
"data_key_reference FROM prefs_index WHERE prefs_index MATCH "
"data_key_reference, payload_type, payload FROM prefs_index WHERE prefs_index MATCH "
+ "'data_title:%s* " +
"OR data_title_normalized:%s* OR data_keywords:%s*' AND locale = 'en_US'",
mQueryText, mQueryText, mQueryText);
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public ArrayList<SearchResult> parseCursorForSearch(Cursor cursorResults) {
if (cursorResults == null) {
return null;
}
final Map<String, Context> contextMap = new HashMap<>();
final ArrayList<SearchResult> results = new ArrayList<>();
while (cursorResults.moveToNext()) {
SearchResult result = buildSingleSearchResultFromCursor(contextMap, cursorResults);
if (result != null) {
results.add(result);
}
}
Collections.sort(results);
return results;
}
private SearchResult buildSingleSearchResultFromCursor(Map<String, Context> contextMap,
Cursor cursor) {
final String pkgName = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE);
final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION);
final String title = cursor.getString(Index.COLUMN_INDEX_TITLE);
final String summaryOn = cursor.getString(Index.COLUMN_INDEX_SUMMARY_ON);
final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME);
final int rank = cursor.getInt(Index.COLUMN_INDEX_RANK);
final String key = cursor.getString(Index.COLUMN_INDEX_KEY);
final String iconResStr = cursor.getString(Index.COLUMN_INDEX_ICON);
final ResultPayload payload;
if (TextUtils.isEmpty(action)) {
final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE);
// Action is null, we will launch it as a sub-setting
final Bundle args = new Bundle();
args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
final Intent intent = Utils.onBuildStartFragmentIntent(mContext,
className, args, null, 0, screenTitle, false);
payload = new IntentPayload(intent);
} else {
final Intent intent = new Intent(action);
final String targetClass = cursor.getString(
Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS);
if (!TextUtils.isEmpty(pkgName) && !TextUtils.isEmpty(targetClass)) {
final ComponentName component = new ComponentName(pkgName, targetClass);
intent.setComponent(component);
}
intent.putExtra(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key);
payload = new IntentPayload(intent);
}
SearchResult.Builder builder = new SearchResult.Builder();
builder.addTitle(title)
.addSummary(summaryOn)
.addRank(rank)
.addIcon(getIconForPackage(contextMap, pkgName, className, iconResStr))
.addPayload(payload);
return builder.build();
}
private Drawable getIconForPackage(Map<String, Context> contextMap, String pkgName,
String className, String iconResStr) {
final int iconId = TextUtils.isEmpty(iconResStr)
? 0 : Integer.parseInt(iconResStr);
Drawable icon;
Context packageContext;
if (iconId == 0) {
icon = null;
} else {
if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(pkgName)) {
packageContext = contextMap.get(pkgName);
if (packageContext == null) {
try {
packageContext = mContext.createPackageContext(pkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG, "Cannot create Context for package: " + pkgName);
return null;
}
contextMap.put(pkgName, packageContext);
}
} else {
packageContext = mContext;
}
try {
icon = packageContext.getDrawable(iconId);
} catch (Resources.NotFoundException nfe) {
icon = null;
}
}
return icon;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.search2;
/**
* Abstract Payload for inline settings results.
*/
public abstract class InlinePayload extends ResultPayload {
/**
* Defines the URI to access and store the Setting the inline result represents
*/
public String settingsUri;
/**
* The UI type for the inline result.
*/
@PayloadType public int inlineType;
/**
* Defines where the Setting is stored.
*/
@SettingsSource public int settingSource;
public InlinePayload(String uri, @PayloadType int type, @SettingsSource int source) {
settingsUri = uri;
inlineType = type;
settingSource = source;
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.search2;
import android.net.Uri;
import android.os.Parcel;
/**
* Payload for Inline Settings results represented by a Slider.
*/
public class InlineSliderPayload extends ResultPayload {
public final Uri uri;
private InlineSliderPayload(Parcel in) {
uri = in.readParcelable(InlineSliderPayload.class.getClassLoader());
}
public InlineSliderPayload(Uri newUri) {
uri = newUri;
}
@Override
public int getType() {
return PayloadType.INLINE_SLIDER;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags);
}
public static final Creator<InlineSliderPayload> CREATOR = new Creator<InlineSliderPayload>() {
@Override
public InlineSliderPayload createFromParcel(Parcel in) {
return new InlineSliderPayload(in);
}
@Override
public InlineSliderPayload[] newArray(int size) {
return new InlineSliderPayload[size];
}
};
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search2;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings;
import android.util.ArrayMap;
import java.util.Map;
/**
* Payload for inline Switch results. Mappings from integer to boolean.
*/
public class InlineSwitchPayload extends InlinePayload {
/**
* Maps Inline values to UI-consumable Values.
* For example, if you have a switch preference whose values are stored as ints, the two valid
* list of mappings would be:
* < (0,True), (1, false) >
* < (1,True), (0, false) >
*/
public final Map<Integer, Boolean> valueMap;
public InlineSwitchPayload(String newUri, @SettingsSource int settingsSource,
ArrayMap<Integer, Boolean> map) {
super(newUri, PayloadType.INLINE_SWITCH, settingsSource);
valueMap = map;
}
private InlineSwitchPayload(Parcel in) {
super(in.readString() /* Uri */ , in.readInt() /* Payload Type */,
in.readInt() /* Settings Source */);
valueMap = in.readHashMap(Integer.class.getClassLoader());
}
@Override
public int getType() {
return inlineType;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(settingsUri);
dest.writeInt(inlineType);
dest.writeInt(settingSource);
dest.writeMap(valueMap);
}
public static final Parcelable.Creator<InlineSwitchPayload> CREATOR =
new Parcelable.Creator<InlineSwitchPayload>() {
@Override
public InlineSwitchPayload createFromParcel(Parcel in) {
return new InlineSwitchPayload(in);
}
@Override
public InlineSwitchPayload[] newArray(int size) {
return new InlineSwitchPayload[size];
}
};
public boolean getSwitchValue(Context context) {
if (valueMap == null) {
throw new IllegalStateException("Value map is null");
}
int settingsValue = -1;
switch(settingSource) {
case SettingsSource.SECURE:
settingsValue = Settings.Secure.getInt(context.getContentResolver(),
settingsUri, 0);
}
if (settingsValue == -1) {
throw new IllegalStateException("Unable to find setting from uri: "
+ settingsUri.toString());
}
for (Integer key : valueMap.keySet()) {
if ((key == settingsValue)) {
return valueMap.get(key);
}
}
throw new IllegalStateException("No results matched the key: " + settingsValue);
}
public void setSwitchValue(Context context, boolean isChecked) {
if (valueMap == null) {
throw new IllegalStateException("Value map is null");
}
int switchValue = -1;
for (Map.Entry<Integer, Boolean> pair : valueMap.entrySet()) {
if (pair.getValue() == isChecked) {
switchValue = pair.getKey();
break;
}
}
if (switchValue == -1) {
throw new IllegalStateException("Switch value is not set");
}
switch(settingSource) {
case SettingsSource.GLOBAL:
return;
case SettingsSource.SECURE:
Settings.Secure.putInt(context.getContentResolver(), settingsUri, switchValue);
case SettingsSource.SYSTEM:
return;
case SettingsSource.UNKNOWN:
return;
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search2;
import android.app.Fragment;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.content.Context;
import com.android.internal.widget.PreferenceImageView;
import com.android.settings.R;
/**
* ViewHolder for Settings represented as SwitchPreferences.
*/
public class InlineSwitchViewHolder extends SearchViewHolder {
public final TextView titleView;
public final TextView summaryView;
public final PreferenceImageView iconView;
public final Switch switchView;
private final Context mContext;
private final String TAG = "SwitchViewHolder";
public InlineSwitchViewHolder(View view, Context context) {
super(view);
mContext = context;
titleView = (TextView) view.findViewById(android.R.id.title);
summaryView = (TextView) view.findViewById(android.R.id.summary);
iconView = (PreferenceImageView) view.findViewById(android.R.id.icon);
switchView = (Switch) view.findViewById(R.id.switchView);
}
@Override
public void onBind(Fragment fragment, SearchResult result) {
if (mContext == null) {
return;
}
final InlineSwitchPayload payload = (InlineSwitchPayload) result.payload;
switchView.setChecked(payload.getSwitchValue(mContext));
switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
payload.setSwitchValue(mContext, isChecked);
}
});
titleView.setText(result.title);
summaryView.setText(result.summary);
iconView.setImageDrawable(result.icon);
}
}

View File

@@ -34,13 +34,13 @@ public class IntentSearchViewHolder extends SearchViewHolder {
public IntentSearchViewHolder(View view) {
super(view);
titleView = (TextView) view.findViewById(R.id.title);
summaryView = (TextView) view.findViewById(R.id.summary);
iconView = (ImageView) view.findViewById(R.id.icon);
titleView = (TextView) view.findViewById(android.R.id.title);
summaryView = (TextView) view.findViewById(android.R.id.summary);
iconView = (ImageView) view.findViewById(android.R.id.icon);
}
@Override
public void onBind(Fragment fragment, SearchResult result) {
public void onBind(final Fragment fragment, final SearchResult result) {
titleView.setText(result.title);
summaryView.setText(result.summary);
iconView.setImageDrawable(result.icon);

View File

@@ -28,7 +28,8 @@ import java.lang.annotation.RetentionPolicy;
*/
public abstract class ResultPayload implements Parcelable {
@IntDef({PayloadType.INLINE_SLIDER, PayloadType.INLINE_SWITCH, PayloadType.INTENT})
@IntDef({PayloadType.INLINE_SLIDER, PayloadType.INLINE_SWITCH,
PayloadType.INTENT})
@Retention(RetentionPolicy.SOURCE)
public @interface PayloadType {
/**
@@ -47,5 +48,16 @@ public abstract class ResultPayload implements Parcelable {
int INLINE_SWITCH = 2;
}
@IntDef({SettingsSource.UNKNOWN, SettingsSource.SYSTEM, SettingsSource.SECURE,
SettingsSource.GLOBAL})
@Retention(RetentionPolicy.SOURCE)
public @interface SettingsSource {
int UNKNOWN = 0;
int SYSTEM = 1;
int SECURE = 2;
int GLOBAL = 3;
}
@ResultPayload.PayloadType public abstract int getType();
}

View File

@@ -16,8 +16,10 @@
package com.android.settings.search2;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.RecyclerView.Adapter;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -26,7 +28,6 @@ import com.android.settings.R;
import com.android.settings.search2.ResultPayload.PayloadType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,22 +39,23 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
public SearchResultsAdapter(SearchFragment fragment) {
mFragment = fragment;
mSearchResults = new ArrayList<>();
mResultsMap = new HashMap<>();
mResultsMap = new ArrayMap<>();
setHasStableIds(true);
}
@Override
public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) {
final Context context = parent.getContext();
final LayoutInflater inflater = LayoutInflater.from(context);
final View view;
switch(viewType) {
case PayloadType.INTENT:
View view = inflater.inflate(R.layout.search_intent_item, parent, false);
view = inflater.inflate(R.layout.search_intent_item, parent, false);
return new IntentSearchViewHolder(view);
case PayloadType.INLINE_SLIDER:
return null;
case PayloadType.INLINE_SWITCH:
return null;
view = inflater.inflate(R.layout.search_inline_switch_item, parent, false);
return new InlineSwitchViewHolder(view, context);
default:
return null;
}

View File

@@ -17,6 +17,7 @@
package com.android.internal.hardware;
import android.provider.Settings;
import android.content.Context;
/**
* Fake controller to make robolectric test compile. Should be removed when Robolectric supports
@@ -24,6 +25,8 @@ import android.provider.Settings;
*/
public class AmbientDisplayConfiguration {
public AmbientDisplayConfiguration(Context context) {}
public boolean pulseOnPickupAvailable() {
return false;
}

View File

@@ -23,6 +23,8 @@ import android.support.v7.preference.PreferenceScreen;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -91,4 +93,23 @@ public class DoubleTapPowerPreferenceControllerTest {
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testPreferenceController_ProperResultPayloadType() {
final Context context = ShadowApplication.getInstance().getApplicationContext();
mController = new DoubleTapPowerPreferenceController(context, null);
ResultPayload payload = mController.getResultPayload();
assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
}
@Test
public void testPreferenceController_CorrectPayload() {
final Context context = ShadowApplication.getInstance().getApplicationContext();
mController = new DoubleTapPowerPreferenceController(context, null);
InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload();
assertThat(payload.settingsUri).isEqualTo("camera_double_tap_power_gesture_disabled");
assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SECURE);
assertThat(payload.valueMap.get(0)).isEqualTo(true);
assertThat(payload.valueMap.get(1)).isEqualTo(false);
}
}

View File

@@ -22,6 +22,8 @@ import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -79,4 +81,19 @@ public class DoubleTapScreenPreferenceControllerTest {
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testPreferenceController_ProperResultPayloadType() {
ResultPayload payload = mController.getResultPayload();
assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
}
@Test
public void testPreferenceController_CorrectPayload() {
InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload();
assertThat(payload.settingsUri).isEqualTo("doze_pulse_on_double_tap");
assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SECURE);
assertThat(payload.valueMap.get(1)).isEqualTo(true);
assertThat(payload.valueMap.get(0)).isEqualTo(false);
}
}

View File

@@ -24,6 +24,8 @@ import android.provider.Settings;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -111,4 +113,19 @@ public class DoubleTwistPreferenceControllerTest {
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testPreferenceController_ProperResultPayloadType() {
ResultPayload payload = mController.getResultPayload();
assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
}
@Test
public void testPreferenceController_CorrectPayload() {
InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload();
assertThat(payload.settingsUri).isEqualTo("camera_double_twist_to_flip_enabled");
assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SECURE);
assertThat(payload.valueMap.get(1)).isEqualTo(true);
assertThat(payload.valueMap.get(0)).isEqualTo(false);
}
}

View File

@@ -22,6 +22,8 @@ import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,4 +84,18 @@ public class PIckupGesturePreferenceControllerTest {
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testPreferenceController_ProperResultPayloadType() {
ResultPayload payload = mController.getResultPayload();
assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
}
@Test
public void testPreferenceController_CorrectPayload() {
InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload();
assertThat(payload.settingsUri).isEqualTo("doze_pulse_on_pick_up");
assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SECURE);
assertThat(payload.valueMap.get(1)).isEqualTo(true);
assertThat(payload.valueMap.get(0)).isEqualTo(false);
}
}

View File

@@ -22,6 +22,8 @@ import android.provider.Settings;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,4 +89,19 @@ public class SwipeToNotificationPreferenceControllerTest {
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testPreferenceController_ProperResultPayloadType() {
ResultPayload payload = mController.getResultPayload();
assertThat(payload).isInstanceOf(InlineSwitchPayload.class);
}
@Test
public void testPreferenceController_CorrectPayload() {
InlineSwitchPayload payload = (InlineSwitchPayload) mController.getResultPayload();
assertThat(payload.settingsUri).isEqualTo("system_navigation_keys_enabled");
assertThat(payload.settingSource).isEqualTo(ResultPayload.SettingsSource.SECURE);
assertThat(payload.valueMap.get(1)).isEqualTo(true);
assertThat(payload.valueMap.get(0)).isEqualTo(false);
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search;
import android.content.Context;
import android.util.ArrayMap;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.PreferenceController;
import com.android.settings.gestures.DoubleTapPowerPreferenceController;
import com.android.settings.gestures.DoubleTapScreenPreferenceController;
import com.android.settings.gestures.DoubleTwistPreferenceController;
import com.android.settings.gestures.PickupGesturePreferenceController;
import com.android.settings.gestures.SwipeToNotificationPreferenceController;
import com.android.settings.search2.DatabaseIndexingUtils;
import com.android.settings.search2.IntentPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.Map;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DatabaseIndexingUtilsTest {
private Context mContext;
@Mock
private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = ShadowApplication.getInstance().getApplicationContext();
}
@Test
public void testGetPreferenceControllerUriMap_BadClassName_ReturnsNull() {
Map map = DatabaseIndexingUtils.getPreferenceControllerUriMap("dummy", mContext);
assertThat(map).isNull();
}
@Test
public void testGetPreferenceControllerUriMap_NullContext_ReturnsNull() {
Map map = DatabaseIndexingUtils.getPreferenceControllerUriMap("dummy", null);
assertThat(map).isNull();
}
@Test
public void testGetPreferenceControllerUriMap_CompatibleClass_ReturnsNotNull() {
String className = "com.android.settings.gestures.GestureSettings";
Map map = DatabaseIndexingUtils.getPreferenceControllerUriMap(className, mContext);
assertThat(map).isNotNull();
}
@Test
public void testGetPreferenceControllerUriMap_CompatibleClass_ReturnsValidMap() {
String className = "com.android.settings.gestures.GestureSettings";
Map map = DatabaseIndexingUtils.getPreferenceControllerUriMap(className, mContext);
assertThat(map.get("gesture_double_twist"))
.isInstanceOf(DoubleTwistPreferenceController.class);
assertThat(map.get("gesture_swipe_down_fingerprint"))
.isInstanceOf(SwipeToNotificationPreferenceController.class);
assertThat(map.get("gesture_double_tap_screen"))
.isInstanceOf(DoubleTapScreenPreferenceController.class);
assertThat(map.get("gesture_pick_up"))
.isInstanceOf(PickupGesturePreferenceController.class);
assertThat(map.get("gesture_double_tap_power"))
.isInstanceOf(DoubleTapPowerPreferenceController.class);
}
@Test
public void testGetPayloadFromMap_NullMap_ReturnsNull() {
ResultPayload payload = DatabaseIndexingUtils.getPayloadFromUriMap(null, "");
assertThat(payload).isNull();
}
@Test
public void testGetPayloadFromMap_MatchingKey_ReturnsPayloa() {
final String key = "key";
PreferenceController prefController = new PreferenceController(mContext) {
@Override
public boolean isAvailable() {
return false;
}
@Override
public String getPreferenceKey() {
return key;
}
@Override
public ResultPayload getResultPayload() {
return new IntentPayload(null);
}
};
ArrayMap<String,PreferenceController> map = new ArrayMap<>();
map.put(key, prefController);
ResultPayload payload = DatabaseIndexingUtils.getPayloadFromUriMap(map, key);
assertThat(payload).isInstanceOf(IntentPayload.class);
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search;
import android.net.Uri;
import android.os.Parcel;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSliderPayload;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class InlineSliderPayloadTest {
private InlineSliderPayload mPayload;
@Test
public void testParcelOrdering_StaysValid() {
Uri uri = Uri.parse("http://www.TESTURI.com");
Parcel parcel = Parcel.obtain();
mPayload = new InlineSliderPayload(uri);
mPayload.writeToParcel(parcel, 0);
// Reset parcel for reading
parcel.setDataPosition(0);
InlineSliderPayload newPayload = InlineSliderPayload.CREATOR.createFromParcel(parcel);
String originalUri = mPayload.uri.toString();
String copiedUri = newPayload.uri.toString();
assertThat(originalUri).isEqualTo(copiedUri);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search;
import android.os.Parcel;
import android.util.ArrayMap;
import android.content.Context;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class InlineSwitchPayloadTest {
@Test
public void testGetSwitch_EmptyMap_ExceptionThrown() {
final String uri = "test.com";
final int source = ResultPayload.SettingsSource.SECURE;
final Context context = ShadowApplication.getInstance().getApplicationContext();
InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, null);
try {
payload.getSwitchValue(context);
fail("Should have thrown exception for null map");
} catch (IllegalStateException e) {
assertThat(e).isNotNull();
}
}
@Test
public void testGetSwitch_BadMap_ExceptionThrown() {
final String uri = "test.com";
final int source = ResultPayload.SettingsSource.SECURE;
final ArrayMap<Integer, Boolean> map = new ArrayMap<>();
final Context context = ShadowApplication.getInstance().getApplicationContext();
InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map);
try {
payload.getSwitchValue(context);
fail("Should have thrown exception for bad map");
} catch (IllegalStateException e) {
assertThat(e).isNotNull();
}
}
@Test
public void testConstructor_DataRetained() {
final String uri = "test.com";
final int type = ResultPayload.PayloadType.INLINE_SWITCH;
final int source = ResultPayload.SettingsSource.SECURE;
final ArrayMap<Integer, Boolean> map = new ArrayMap<>();
map.put(1, true);
map.put(0, false);
InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map);
assertThat(payload.settingsUri).isEqualTo(uri);
assertThat(payload.inlineType).isEqualTo(type);
assertThat(payload.settingSource).isEqualTo(source);
assertThat(payload.valueMap.get(1)).isTrue();
assertThat(payload.valueMap.get(0)).isFalse();
}
@Test
public void testParcelConstructor_DataRetained() {
String uri = "test.com";
int type = ResultPayload.PayloadType.INLINE_SWITCH;
int source = ResultPayload.SettingsSource.SECURE;
final ArrayMap<Integer, Boolean> map = new ArrayMap<>();
map.put(1, true);
map.put(0, false);
Parcel parcel = Parcel.obtain();
parcel.writeString(uri);
parcel.writeInt(type);
parcel.writeInt(source);
parcel.writeMap(map);
parcel.setDataPosition(0);
InlineSwitchPayload payload = InlineSwitchPayload.CREATOR.createFromParcel(parcel);
assertThat(payload.settingsUri).isEqualTo(uri);
assertThat(payload.inlineType).isEqualTo(type);
assertThat(payload.settingSource).isEqualTo(source);
assertThat(payload.valueMap.get(1)).isTrue();
assertThat(payload.valueMap.get(0)).isFalse();
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.settings.search;
import android.app.Fragment;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.InlineSwitchViewHolder;
import com.android.settings.search2.IntentPayload;
import com.android.settings.search2.SearchResult;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class InlineSwitchViewHolderTest {
private static final String TITLE = "title";
private static final String SUMMARY = "summary";
@Mock
private Fragment mFragment;
@Mock
private InlineSwitchPayload mPayload;
private InlineSwitchViewHolder mHolder;
private Drawable mIcon;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
final Context context = ShadowApplication.getInstance().getApplicationContext();
View view = LayoutInflater.from(context).inflate(R.layout.search_inline_switch_item, null);
mHolder = new InlineSwitchViewHolder(view, context);
mIcon = context.getDrawable(R.drawable.ic_search_history);
}
@Test
public void testConstructor_MembersNotNull() {
assertThat(mHolder.titleView).isNotNull();
assertThat(mHolder.summaryView).isNotNull();
assertThat(mHolder.iconView).isNotNull();
assertThat(mHolder.switchView).isNotNull();
}
@Test
public void testBindViewElements_AllUpdated() {
when(mPayload.getSwitchValue(any(Context.class))).thenReturn(true);
SearchResult result = getSearchResult();
mHolder.onBind(mFragment, result);
mHolder.switchView.setChecked(true);
assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
assertThat(mHolder.iconView.getDrawable()).isEqualTo(mIcon);
assertThat(mHolder.switchView.isChecked()).isTrue();
}
private SearchResult getSearchResult() {
SearchResult.Builder builder = new SearchResult.Builder();
builder.addTitle(TITLE)
.addSummary(SUMMARY)
.addRank(1)
.addPayload(new InlineSwitchPayload("", 0, null))
.addBreadcrumbs(new ArrayList<String>())
.addIcon(mIcon)
.addPayload(mPayload);
return builder.build();
}
}

View File

@@ -93,7 +93,7 @@ public class IntentSearchViewHolderTest {
.addSummary(SUMMARY)
.addRank(1)
.addPayload(new IntentPayload(null))
.addBreadcrumbs(new ArrayList<>())
.addBreadcrumbs(new ArrayList<String>())
.addIcon(mIcon);
return builder.build();

View File

@@ -21,17 +21,22 @@ import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search2.DatabaseResultLoader;
import com.android.settings.search2.InlineSwitchViewHolder;
import com.android.settings.search2.IntentPayload;
import com.android.settings.search2.IntentSearchViewHolder;
import com.android.settings.search2.ResultPayload;
import com.android.settings.search2.SearchFragment;
import com.android.settings.search2.SearchResult;
import com.android.settings.search2.SearchResult.Builder;
import com.android.settings.search2.SearchResultsAdapter;
import com.android.settings.search2.SearchViewHolder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +44,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowViewGroup;
import java.util.ArrayList;
import java.util.List;
@@ -63,6 +70,47 @@ public class SearchAdapterTest {
mLoaderClassName = DatabaseResultLoader.class.getName();
}
@Test
public void testNoResultsAdded_EmptyListReturned() {
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).isEmpty();
}
@Test
public void testSingleSourceMerge_ExactCopyReturned() {
ArrayList<SearchResult> intentResults = getIntentSampleResults();
mAdapter.mergeResults(intentResults, mLoaderClassName);
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).containsAllIn(intentResults);
}
@Test
public void testDuplicateSourceMerge_ExactCopyReturned() {
ArrayList<SearchResult> intentResults = getIntentSampleResults();
mAdapter.mergeResults(intentResults, mLoaderClassName);
mAdapter.mergeResults(intentResults, mLoaderClassName);
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).containsAllIn(intentResults);
}
@Test
public void testCreatViewHolder_ReturnsIntentResult() {
ViewGroup group = new FrameLayout(mContext);
SearchViewHolder view = mAdapter.onCreateViewHolder(group,
ResultPayload.PayloadType.INTENT);
assertThat(view).isInstanceOf(IntentSearchViewHolder.class);
}
@Test
public void testCreatViewHolder_ReturnsInlineSwitchResult() {
ViewGroup group = new FrameLayout(mContext);
SearchViewHolder view = mAdapter.onCreateViewHolder(group,
ResultPayload.PayloadType.INLINE_SWITCH);
assertThat(view).isInstanceOf(InlineSwitchViewHolder.class);
}
private ArrayList<SearchResult> getIntentSampleResults() {
ArrayList<SearchResult> sampleResults = new ArrayList<>();
ArrayList<String> breadcrumbs = new ArrayList<>();
@@ -84,31 +132,4 @@ public class SearchAdapterTest {
sampleResults.add(builder.build());
return sampleResults;
}
@Test
public void testNoResultsAdded_EmptyListReturned() {
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).isEmpty();
}
@Test
public void testSingleSourceMerge_ExactCopyReturned() {
ArrayList<SearchResult> intentResults = getIntentSampleResults();
mAdapter.mergeResults(intentResults, mLoaderClassName);
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).containsAllIn(intentResults);
}
@Test
public void testDuplicateSourceMerge_ExactCopyReturned() {
ArrayList<SearchResult> intentResults = getIntentSampleResults();
mAdapter.mergeResults(intentResults, mLoaderClassName);
mAdapter.mergeResults(intentResults, mLoaderClassName);
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).containsAllIn(intentResults);
}
}

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.settings.search;
package com.android.settings.search2;
import android.app.Activity;
import android.content.Context;
@@ -23,16 +23,13 @@ import android.content.Intent;
import android.database.MatrixCursor;
import android.graphics.drawable.Drawable;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.SubSettings;
import com.android.settings.TestConfig;
import com.android.settings.gestures.GestureSettings;
import com.android.settings.search2.DatabaseResultLoader;
import com.android.settings.search2.IntentPayload;
import com.android.settings.search2.ResultPayload;
import com.android.settings.search2.ResultPayload.PayloadType;
import com.android.settings.search2.SearchResult;
import org.junit.Before;
import org.junit.Test;
@@ -47,12 +44,14 @@ import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DatabaseResultLoaderTest {
private DatabaseResultLoader mLoader;
public class CursorToSearchResultConverterTest {
private CursorToSearchResultConverter mConverter;
private static final String[] COLUMNS = new String[]{"rank", "title", "summary_on",
"summary off", "entries", "keywords", "class name", "screen title", "icon",
"intent action", "target package", "target class", "enabled", "key", "user id"};
"intent action", "target package", "target class", "enabled", "key",
"payload_type", "payload"};
private static final String[] TITLES = new String[]{"title1", "title2", "title3"};
private static final String SUMMARY = "SUMMARY";
@@ -66,24 +65,24 @@ public class DatabaseResultLoaderTest {
public void setUp() {
Context context = Robolectric.buildActivity(Activity.class).get();
mDrawable = context.getDrawable(mIcon);
mLoader = new DatabaseResultLoader(context, "");
mConverter = new CursorToSearchResultConverter(context);
}
@Test
public void testParseNullResults_ReturnsNull() {
List<SearchResult> results = mLoader.parseCursorForSearch(null);
List<SearchResult> results = mConverter.convertCursor(null);
assertThat(results).isNull();
}
@Test
public void testParseCursor_NotNull() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
assertThat(results).isNotNull();
}
@Test
public void testParseCursor_MatchesRank() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).rank).isEqualTo(i);
}
@@ -91,7 +90,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_MatchesTitle() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).title).isEqualTo(TITLES[i]);
}
@@ -99,7 +98,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_MatchesSummary() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
for (int i = 0; i < EXAMPLES; i++) {
assertThat(results.get(i).summary).isEqualTo(SUMMARY);
}
@@ -107,7 +106,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_MatchesIcon() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon;
assertThat(resultDrawable.toString()).isEqualTo(mDrawable.toString());
@@ -116,7 +115,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_NoIcon() {
List<SearchResult> results = mLoader.parseCursorForSearch(
List<SearchResult> results = mConverter.convertCursor(
getDummyCursor(false /* hasIcon */));
for (int i = 0; i < EXAMPLES; i++) {
Drawable resultDrawable = results.get(i).icon;
@@ -126,7 +125,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_MatchesPayloadType() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
ResultPayload payload;
for (int i = 0; i < EXAMPLES; i++) {
payload = results.get(i).payload;
@@ -153,9 +152,10 @@ public class DatabaseResultLoaderTest {
BLANK, // target class
BLANK, // enabled
BLANK, // key
BLANK // user id
0, // Payload Type
null // Payload
});
List<SearchResult> results = mLoader.parseCursorForSearch(cursor);
List<SearchResult> results = mConverter.convertCursor(cursor);
IntentPayload payload = (IntentPayload) results.get(0).payload;
Intent intent = payload.intent;
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
@@ -163,7 +163,7 @@ public class DatabaseResultLoaderTest {
@Test
public void testParseCursor_MatchesIntentPayload() {
List<SearchResult> results = mLoader.parseCursorForSearch(getDummyCursor());
List<SearchResult> results = mConverter.convertCursor(getDummyCursor());
IntentPayload payload;
for (int i = 0; i < EXAMPLES; i++) {
payload = (IntentPayload) results.get(i).payload;
@@ -172,6 +172,46 @@ public class DatabaseResultLoaderTest {
}
}
@Test
public void testParseCursor_MatchesInlineSwitchPayload() {
MatrixCursor cursor = new MatrixCursor(COLUMNS);
final String BLANK = "";
final String uri = "test.com";
final int type = ResultPayload.PayloadType.INLINE_SWITCH;
final int source = ResultPayload.SettingsSource.SECURE;
final ArrayMap<Integer, Boolean> map = new ArrayMap<>();
map.put(1, true);
map.put(0, false);
final InlineSwitchPayload payload = new InlineSwitchPayload(uri, source, map);
cursor.addRow(new Object[]{
0, // rank
TITLES[0],
SUMMARY,
SUMMARY, // summary off
BLANK, // entries
BLANK, // Keywords
GestureSettings.class.getName(),
BLANK, // screen title
null, // icon
BLANK, // action
null, // target package
BLANK, // target class
BLANK, // enabled
BLANK, // key
type, // Payload Type
ResultPayloadUtils.marshall(payload) // Payload
});
List<SearchResult> results = mConverter.convertCursor(cursor);
InlineSwitchPayload newPayload = (InlineSwitchPayload) results.get(0).payload;
assertThat(newPayload.settingsUri).isEqualTo(uri);
assertThat(newPayload.inlineType).isEqualTo(type);
assertThat(newPayload.settingSource).isEqualTo(source);
assertThat(newPayload.valueMap.get(1)).isTrue();
assertThat(newPayload.valueMap.get(0)).isFalse();
}
private MatrixCursor getDummyCursor() {
return getDummyCursor(true /* hasIcon */);
}
@@ -196,7 +236,10 @@ public class DatabaseResultLoaderTest {
item.add(BLANK); // target class
item.add(BLANK); // enabled
item.add(BLANK); // key
item.add(BLANK); // user id
// Note there is no user id. This is omitted because it is not being
// queried. Should the queries change, so should this method.
item.add(Integer.toString(0)); // Payload Type
item.add(null); // Payload
cursor.addRow(item);
}