Snap for 4648297 from decea2055d to pi-release

Change-Id: I05ca8725fe9d9ee0a1efc1bd3d9d2f0a55424001
This commit is contained in:
android-build-team Robot
2018-03-12 07:23:24 +00:00
20 changed files with 368 additions and 416 deletions

View File

@@ -3231,7 +3231,7 @@
</activity> </activity>
<provider android:name=".slices.SettingsSliceProvider" <provider android:name=".slices.SettingsSliceProvider"
android:authorities="com.android.settings.slices" android:authorities="com.android.settings.slices;android.settings.slices"
android:exported="true"> android:exported="true">
</provider> </provider>

View File

@@ -96,8 +96,12 @@
<!-- For Search --> <!-- For Search -->
<declare-styleable name="Preference"> <declare-styleable name="Preference">
<!-- Synonyms for search results -->
<attr name="keywords" format="string" /> <attr name="keywords" format="string" />
<!-- Classname of a PreferenceController corresponding to the preference -->
<attr name="controller" format="string" /> <attr name="controller" format="string" />
<!-- {@code true} when the controller declared represents a slice from {@link android.app.SettingsSliceContract} -->
<attr name="platform_slice" format="boolean" />
</declare-styleable> </declare-styleable>
<!-- For DotsPageIndicator --> <!-- For DotsPageIndicator -->

View File

@@ -4177,10 +4177,6 @@
<string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string> <string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] --> <!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
<string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string> <string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string>
<!--
Format string for a physical device in the form: InputMethodSubtype - InputMethodEditor.
e.g. English (US) - X Keyboard -->
<string name="physical_device_title" translatable="false"><xliff:g id="input_method_editor" example="X Keyboard">%1$s</xliff:g> - <xliff:g id="input_method_subtype" example="English (US)">%2$s</xliff:g></string>
<!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] --> <!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
<string name="default_keyboard_layout">Default</string> <string name="default_keyboard_layout">Default</string>

View File

@@ -26,7 +26,8 @@
android:icon="@drawable/ic_settings_bluetooth" android:icon="@drawable/ic_settings_bluetooth"
android:summary="@string/bluetooth_pref_summary" android:summary="@string/bluetooth_pref_summary"
android:order="-7" android:order="-7"
settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"/> settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"
settings:platform_slice="true"/>
<SwitchPreference <SwitchPreference
android:key="toggle_nfc" android:key="toggle_nfc"

View File

@@ -76,6 +76,7 @@ public class PreferenceXmlParserUtils {
int FLAG_NEED_PREF_TITLE = 1 << 4; int FLAG_NEED_PREF_TITLE = 1 << 4;
int FLAG_NEED_PREF_SUMMARY = 1 << 5; int FLAG_NEED_PREF_SUMMARY = 1 << 5;
int FLAG_NEED_PREF_ICON = 1 << 6; int FLAG_NEED_PREF_ICON = 1 << 6;
int FLAG_NEED_PLATFORM_SLICE_FLAG = 1 << 7;
} }
public static final String METADATA_PREF_TYPE = "type"; public static final String METADATA_PREF_TYPE = "type";
@@ -84,35 +85,48 @@ public class PreferenceXmlParserUtils {
public static final String METADATA_TITLE = "title"; public static final String METADATA_TITLE = "title";
public static final String METADATA_SUMMARY = "summary"; public static final String METADATA_SUMMARY = "summary";
public static final String METADATA_ICON = "icon"; public static final String METADATA_ICON = "icon";
public static final String METADATA_PLATFORM_SLICE_FLAG = "platform_slice";
private static final String ENTRIES_SEPARATOR = "|"; private static final String ENTRIES_SEPARATOR = "|";
/**
* Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_KEY} instead.
*/
@Deprecated
public static String getDataKey(Context context, AttributeSet attrs) { public static String getDataKey(Context context, AttributeSet attrs) {
return getData(context, attrs, return getStringData(context, attrs,
com.android.internal.R.styleable.Preference, com.android.internal.R.styleable.Preference,
com.android.internal.R.styleable.Preference_key); com.android.internal.R.styleable.Preference_key);
} }
/**
* Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_TITLE} instead.
*/
@Deprecated
public static String getDataTitle(Context context, AttributeSet attrs) { public static String getDataTitle(Context context, AttributeSet attrs) {
return getData(context, attrs, return getStringData(context, attrs,
com.android.internal.R.styleable.Preference, com.android.internal.R.styleable.Preference,
com.android.internal.R.styleable.Preference_title); com.android.internal.R.styleable.Preference_title);
} }
/**
* Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_SUMMARY} instead.
*/
@Deprecated
public static String getDataSummary(Context context, AttributeSet attrs) { public static String getDataSummary(Context context, AttributeSet attrs) {
return getData(context, attrs, return getStringData(context, attrs,
com.android.internal.R.styleable.Preference, com.android.internal.R.styleable.Preference,
com.android.internal.R.styleable.Preference_summary); com.android.internal.R.styleable.Preference_summary);
} }
public static String getDataSummaryOn(Context context, AttributeSet attrs) { public static String getDataSummaryOn(Context context, AttributeSet attrs) {
return getData(context, attrs, return getStringData(context, attrs,
com.android.internal.R.styleable.CheckBoxPreference, com.android.internal.R.styleable.CheckBoxPreference,
com.android.internal.R.styleable.CheckBoxPreference_summaryOn); com.android.internal.R.styleable.CheckBoxPreference_summaryOn);
} }
public static String getDataSummaryOff(Context context, AttributeSet attrs) { public static String getDataSummaryOff(Context context, AttributeSet attrs) {
return getData(context, attrs, return getStringData(context, attrs,
com.android.internal.R.styleable.CheckBoxPreference, com.android.internal.R.styleable.CheckBoxPreference,
com.android.internal.R.styleable.CheckBoxPreference_summaryOff); com.android.internal.R.styleable.CheckBoxPreference_summaryOff);
} }
@@ -124,13 +138,23 @@ public class PreferenceXmlParserUtils {
} }
public static String getDataKeywords(Context context, AttributeSet attrs) { public static String getDataKeywords(Context context, AttributeSet attrs) {
return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_keywords); return getStringData(context, attrs, R.styleable.Preference,
R.styleable.Preference_keywords);
} }
/**
* Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_CONTROLLER} instead.
*/
@Deprecated
public static String getController(Context context, AttributeSet attrs) { public static String getController(Context context, AttributeSet attrs) {
return getData(context, attrs, R.styleable.Preference, R.styleable.Preference_controller); return getStringData(context, attrs, R.styleable.Preference,
R.styleable.Preference_controller);
} }
/**
* Call {@link #extractMetadata(Context, int, int)} with {@link #METADATA_ICON} instead.
*/
@Deprecated
public static int getDataIcon(Context context, AttributeSet attrs) { public static int getDataIcon(Context context, AttributeSet attrs) {
final TypedArray ta = context.obtainStyledAttributes(attrs, final TypedArray ta = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Preference); com.android.internal.R.styleable.Preference);
@@ -176,25 +200,35 @@ public class PreferenceXmlParserUtils {
} }
final Bundle preferenceMetadata = new Bundle(); final Bundle preferenceMetadata = new Bundle();
final AttributeSet attrs = Xml.asAttributeSet(parser); final AttributeSet attrs = Xml.asAttributeSet(parser);
final TypedArray preferenceAttributes = context.obtainStyledAttributes(attrs,
R.styleable.Preference);
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TYPE)) {
preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName); preferenceMetadata.putString(METADATA_PREF_TYPE, nodeName);
} }
if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEY)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_KEY)) {
preferenceMetadata.putString(METADATA_KEY, getDataKey(context, attrs)); preferenceMetadata.putString(METADATA_KEY, getKey(preferenceAttributes));
} }
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_CONTROLLER)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_CONTROLLER)) {
preferenceMetadata.putString(METADATA_CONTROLLER, getController(context, attrs)); preferenceMetadata.putString(METADATA_CONTROLLER,
getController(preferenceAttributes));
} }
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_TITLE)) {
preferenceMetadata.putString(METADATA_TITLE, getDataTitle(context, attrs)); preferenceMetadata.putString(METADATA_TITLE, getTitle(preferenceAttributes));
} }
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_SUMMARY)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_SUMMARY)) {
preferenceMetadata.putString(METADATA_SUMMARY, getDataSummary(context, attrs)); preferenceMetadata.putString(METADATA_SUMMARY, getSummary(preferenceAttributes));
} }
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_ICON)) { if (hasFlag(flags, MetadataFlag.FLAG_NEED_PREF_ICON)) {
preferenceMetadata.putInt(METADATA_ICON, getDataIcon(context, attrs)); preferenceMetadata.putInt(METADATA_ICON, getIcon(preferenceAttributes));
}
if (hasFlag(flags, MetadataFlag.FLAG_NEED_PLATFORM_SLICE_FLAG)) {
preferenceMetadata.putBoolean(METADATA_PLATFORM_SLICE_FLAG,
getPlatformSlice(preferenceAttributes));
} }
metadata.add(preferenceMetadata); metadata.add(preferenceMetadata);
preferenceAttributes.recycle();
} while ((type = parser.next()) != XmlPullParser.END_DOCUMENT } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)); && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
parser.close(); parser.close();
@@ -205,12 +239,16 @@ public class PreferenceXmlParserUtils {
* Returns the fragment name if this preference launches a child fragment. * Returns the fragment name if this preference launches a child fragment.
*/ */
public static String getDataChildFragment(Context context, AttributeSet attrs) { public static String getDataChildFragment(Context context, AttributeSet attrs) {
return getData(context, attrs, R.styleable.Preference, return getStringData(context, attrs, R.styleable.Preference,
R.styleable.Preference_android_fragment); R.styleable.Preference_android_fragment);
} }
/**
* Call {@link #extractMetadata(Context, int, int)} with a {@link MetadataFlag} instead.
*/
@Deprecated
@Nullable @Nullable
private static String getData(Context context, AttributeSet set, int[] attrs, int resId) { private static String getStringData(Context context, AttributeSet set, int[] attrs, int resId) {
final TypedArray ta = context.obtainStyledAttributes(set, attrs); final TypedArray ta = context.obtainStyledAttributes(set, attrs);
String data = ta.getString(resId); String data = ta.getString(resId);
ta.recycle(); ta.recycle();
@@ -243,4 +281,28 @@ public class PreferenceXmlParserUtils {
} }
return result.toString(); return result.toString();
} }
private static String getKey(TypedArray styledAttributes) {
return styledAttributes.getString(com.android.internal.R.styleable.Preference_key);
}
private static String getTitle(TypedArray styledAttributes) {
return styledAttributes.getString(com.android.internal.R.styleable.Preference_title);
}
private static String getSummary(TypedArray styledAttributes) {
return styledAttributes.getString(com.android.internal.R.styleable.Preference_summary);
}
private static String getController(TypedArray styledAttributes) {
return styledAttributes.getString(R.styleable.Preference_controller);
}
private static int getIcon(TypedArray styledAttributes) {
return styledAttributes.getResourceId(com.android.internal.R.styleable.Icon_icon, 0);
}
private static boolean getPlatformSlice(TypedArray styledAttributes) {
return styledAttributes.getBoolean(R.styleable.Preference_platform_slice, false /* def */);
}
} }

View File

@@ -16,43 +16,31 @@
package com.android.settings.inputmethod; package com.android.settings.inputmethod;
import android.annotation.Nullable; import android.content.Context;
import android.app.Activity;
import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.input.InputManager.InputDeviceListener;
import android.hardware.input.KeyboardLayout; import android.hardware.input.KeyboardLayout;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment.KeyboardInfoPreference;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragment public class KeyboardLayoutPickerFragment extends SettingsPreferenceFragment
implements InputDeviceListener { implements InputDeviceListener {
private InputDeviceIdentifier mInputDeviceIdentifier; private InputDeviceIdentifier mInputDeviceIdentifier;
private int mInputDeviceId = -1; private int mInputDeviceId = -1;
private InputManager mIm; private InputManager mIm;
private InputMethodInfo mImi;
@Nullable
private InputMethodSubtype mSubtype;
private KeyboardLayout[] mKeyboardLayouts; private KeyboardLayout[] mKeyboardLayouts;
private Map<Preference, KeyboardLayout> mPreferenceMap = new HashMap<>(); private HashMap<CheckBoxPreference, KeyboardLayout> mPreferenceMap = new HashMap<>();
// TODO: Make these constants public API for b/25752827
/** /**
* Intent extra: The input device descriptor of the keyboard whose keyboard * Intent extra: The input device descriptor of the keyboard whose keyboard
@@ -60,16 +48,6 @@ public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragme
*/ */
public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier"; public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
/**
* Intent extra: The associated {@link InputMethodInfo}.
*/
public static final String EXTRA_INPUT_METHOD_INFO = "input_method_info";
/**
* Intent extra: The associated {@link InputMethodSubtype}.
*/
public static final String EXTRA_INPUT_METHOD_SUBTYPE = "input_method_subtype";
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
return MetricsEvent.INPUTMETHOD_KEYBOARD; return MetricsEvent.INPUTMETHOD_KEYBOARD;
@@ -78,18 +56,14 @@ public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragme
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
Activity activity = Preconditions.checkNotNull(getActivity());
mInputDeviceIdentifier = activity.getIntent().getParcelableExtra( mInputDeviceIdentifier = getActivity().getIntent().getParcelableExtra(
EXTRA_INPUT_DEVICE_IDENTIFIER); EXTRA_INPUT_DEVICE_IDENTIFIER);
mImi = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_INFO); if (mInputDeviceIdentifier == null) {
mSubtype = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_SUBTYPE); getActivity().finish();
if (mInputDeviceIdentifier == null || mImi == null) {
activity.finish();
} }
mIm = activity.getSystemService(InputManager.class); mIm = (InputManager) getSystemService(Context.INPUT_SERVICE);
mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier); mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier);
Arrays.sort(mKeyboardLayouts); Arrays.sort(mKeyboardLayouts);
setPreferenceScreen(createPreferenceHierarchy()); setPreferenceScreen(createPreferenceHierarchy());
@@ -108,6 +82,8 @@ public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragme
return; return;
} }
mInputDeviceId = inputDevice.getId(); mInputDeviceId = inputDevice.getId();
updateCheckedState();
} }
@Override @Override
@@ -120,21 +96,34 @@ public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragme
@Override @Override
public boolean onPreferenceTreeClick(Preference preference) { public boolean onPreferenceTreeClick(Preference preference) {
KeyboardLayout layout = mPreferenceMap.get(preference); if (preference instanceof CheckBoxPreference) {
if (layout != null) { CheckBoxPreference checkboxPref = (CheckBoxPreference)preference;
mIm.setKeyboardLayoutForInputDevice(mInputDeviceIdentifier, mImi, mSubtype, KeyboardLayout layout = mPreferenceMap.get(checkboxPref);
layout.getDescriptor()); if (layout != null) {
getActivity().finish(); boolean checked = checkboxPref.isChecked();
return true; if (checked) {
mIm.addKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
layout.getDescriptor());
} else {
mIm.removeKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
layout.getDescriptor());
}
return true;
}
} }
return super.onPreferenceTreeClick(preference); return super.onPreferenceTreeClick(preference);
} }
@Override @Override
public void onInputDeviceAdded(int deviceId) {} public void onInputDeviceAdded(int deviceId) {
}
@Override @Override
public void onInputDeviceChanged(int deviceId) {} public void onInputDeviceChanged(int deviceId) {
if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
updateCheckedState();
}
}
@Override @Override
public void onInputDeviceRemoved(int deviceId) { public void onInputDeviceRemoved(int deviceId) {
@@ -147,14 +136,23 @@ public final class KeyboardLayoutPickerFragment extends SettingsPreferenceFragme
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity()); PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
for (KeyboardLayout layout : mKeyboardLayouts) { for (KeyboardLayout layout : mKeyboardLayouts) {
Preference pref = new Preference(getPrefContext()); CheckBoxPreference pref = new CheckBoxPreference(getPrefContext());
pref.setTitle(layout.getLabel()); pref.setTitle(layout.getLabel());
pref.setSummary(layout.getCollection()); pref.setSummary(layout.getCollection());
root.addPreference(pref); root.addPreference(pref);
mPreferenceMap.put(pref, layout); mPreferenceMap.put(pref, layout);
} }
root.setTitle(KeyboardInfoPreference.getDisplayName(getContext(), mImi, mSubtype));
return root; return root;
} }
private void updateCheckedState() {
String[] enabledKeyboardLayouts = mIm.getEnabledKeyboardLayoutsForInputDevice(
mInputDeviceIdentifier);
Arrays.sort(enabledKeyboardLayouts);
for (Map.Entry<CheckBoxPreference, KeyboardLayout> entry : mPreferenceMap.entrySet()) {
entry.getKey().setChecked(Arrays.binarySearch(enabledKeyboardLayouts,
entry.getValue().getDescriptor()) >= 0);
}
}
} }

View File

@@ -19,11 +19,8 @@ package com.android.settings.inputmethod;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity; import android.app.Activity;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.Loader;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
@@ -40,9 +37,6 @@ import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.inputmethod.InputMethodUtils;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -52,33 +46,25 @@ import com.android.settings.Settings;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable; import com.android.settings.search.Indexable;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; import com.android.settingslib.utils.ThreadUtils;
import java.text.Collator; import java.text.Collator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
implements InputManager.InputDeviceListener, Indexable { implements InputManager.InputDeviceListener,
KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable {
private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category"; private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category";
private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch"; private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch";
private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper"; private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper";
private static final String IM_SUBTYPE_MODE_KEYBOARD = "keyboard";
@NonNull @NonNull
private final List<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>(); private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
@NonNull
private final List<KeyboardInfoPreference> mTempKeyboardInfoList = new ArrayList<>();
@NonNull
private final HashSet<Integer> mLoaderIDs = new HashSet<>();
private int mNextLoaderId = 0;
private InputManager mIm; private InputManager mIm;
@NonNull @NonNull
@@ -88,6 +74,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
@NonNull @NonNull
private InputMethodUtils.InputMethodSettings mSettings; private InputMethodUtils.InputMethodSettings mSettings;
private Intent mIntentWaitingForResult;
@Override @Override
public void onCreatePreferences(Bundle bundle, String s) { public void onCreatePreferences(Bundle bundle, String s) {
Activity activity = Preconditions.checkNotNull(getActivity()); Activity activity = Preconditions.checkNotNull(getActivity());
@@ -118,9 +106,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
clearLoader();
mLastHardKeyboards.clear(); mLastHardKeyboards.clear();
updateHardKeyboards(); scheduleUpdateHardKeyboards();
mIm.registerInputDeviceListener(this, null); mIm.registerInputDeviceListener(this, null);
mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener( mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(
mShowVirtualKeyboardSwitchPreferenceChangeListener); mShowVirtualKeyboardSwitchPreferenceChangeListener);
@@ -130,67 +117,25 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
clearLoader();
mLastHardKeyboards.clear(); mLastHardKeyboards.clear();
mIm.unregisterInputDeviceListener(this); mIm.unregisterInputDeviceListener(this);
mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(null); mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(null);
unregisterShowVirtualKeyboardSettingsObserver(); unregisterShowVirtualKeyboardSettingsObserver();
} }
private void onLoadFinishedInternal(
final int loaderId, @NonNull final List<Keyboards> keyboardsList) {
if (!mLoaderIDs.remove(loaderId)) {
// Already destroyed loader. Ignore.
return;
}
Collections.sort(keyboardsList);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.removeAll();
for (Keyboards keyboards : keyboardsList) {
final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
category.setTitle(keyboards.mDeviceInfo.mDeviceName);
category.setOrder(0);
preferenceScreen.addPreference(category);
for (Keyboards.KeyboardInfo info : keyboards.mKeyboardInfoList) {
mTempKeyboardInfoList.clear();
final InputMethodInfo imi = info.mImi;
final InputMethodSubtype imSubtype = info.mImSubtype;
if (imi != null) {
KeyboardInfoPreference pref =
new KeyboardInfoPreference(getPrefContext(), info);
pref.setOnPreferenceClickListener(preference -> {
showKeyboardLayoutScreen(
keyboards.mDeviceInfo.mDeviceIdentifier, imi, imSubtype);
return true;
});
mTempKeyboardInfoList.add(pref);
Collections.sort(mTempKeyboardInfoList);
}
for (KeyboardInfoPreference pref : mTempKeyboardInfoList) {
category.addPreference(pref);
}
}
}
mTempKeyboardInfoList.clear();
mKeyboardAssistanceCategory.setOrder(1);
preferenceScreen.addPreference(mKeyboardAssistanceCategory);
updateShowVirtualKeyboardSwitch();
}
@Override @Override
public void onInputDeviceAdded(int deviceId) { public void onInputDeviceAdded(int deviceId) {
updateHardKeyboards(); scheduleUpdateHardKeyboards();
} }
@Override @Override
public void onInputDeviceRemoved(int deviceId) { public void onInputDeviceRemoved(int deviceId) {
updateHardKeyboards(); scheduleUpdateHardKeyboards();
} }
@Override @Override
public void onInputDeviceChanged(int deviceId) { public void onInputDeviceChanged(int deviceId) {
updateHardKeyboards(); scheduleUpdateHardKeyboards();
} }
@Override @Override
@@ -198,50 +143,57 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
return MetricsEvent.PHYSICAL_KEYBOARDS; return MetricsEvent.PHYSICAL_KEYBOARDS;
} }
@NonNull private void scheduleUpdateHardKeyboards() {
public static List<HardKeyboardDeviceInfo> getHardKeyboards() { final Context context = getContext();
final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>(); ThreadUtils.postOnBackgroundThread(() -> {
final int[] devicesIds = InputDevice.getDeviceIds(); final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards(context);
for (int deviceId : devicesIds) { ThreadUtils.postOnMainThread(() -> updateHardKeyboards(newHardKeyboards));
final InputDevice device = InputDevice.getDevice(deviceId); });
if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
keyboards.add(new HardKeyboardDeviceInfo(device.getName(), device.getIdentifier()));
}
}
return keyboards;
} }
private void updateHardKeyboards() { private void updateHardKeyboards(@NonNull List<HardKeyboardDeviceInfo> newHardKeyboards) {
final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards(); if (Objects.equals(mLastHardKeyboards, newHardKeyboards)) {
if (!Objects.equals(newHardKeyboards, mLastHardKeyboards)) { // Nothing has changed. Ignore.
clearLoader(); return;
mLastHardKeyboards.clear();
mLastHardKeyboards.addAll(newHardKeyboards);
mLoaderIDs.add(mNextLoaderId);
getLoaderManager().initLoader(mNextLoaderId, null,
new Callbacks(getContext(), this, mLastHardKeyboards));
++mNextLoaderId;
} }
// TODO(yukawa): Maybe we should follow the style used in ConnectedDeviceDashboardFragment.
mLastHardKeyboards.clear();
mLastHardKeyboards.addAll(newHardKeyboards);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.removeAll();
final PreferenceCategory category = new PreferenceCategory(getPrefContext());
category.setTitle(R.string.builtin_keyboard_settings_title);
category.setOrder(0);
preferenceScreen.addPreference(category);
for (HardKeyboardDeviceInfo hardKeyboardDeviceInfo : newHardKeyboards) {
// TODO(yukawa): Consider using com.android.settings.widget.GearPreference
final Preference pref = new Preference(getPrefContext());
pref.setTitle(hardKeyboardDeviceInfo.mDeviceName);
pref.setSummary(hardKeyboardDeviceInfo.mLayoutLabel);
pref.setOnPreferenceClickListener(preference -> {
showKeyboardLayoutDialog(hardKeyboardDeviceInfo.mDeviceIdentifier);
return true;
});
category.addPreference(pref);
}
mKeyboardAssistanceCategory.setOrder(1);
preferenceScreen.addPreference(mKeyboardAssistanceCategory);
updateShowVirtualKeyboardSwitch();
} }
private void showKeyboardLayoutScreen( private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
@NonNull InputDeviceIdentifier inputDeviceIdentifier, KeyboardLayoutDialogFragment fragment = (KeyboardLayoutDialogFragment)
@NonNull InputMethodInfo imi, getFragmentManager().findFragmentByTag("keyboardLayout");
@Nullable InputMethodSubtype imSubtype) { if (fragment == null) {
final Intent intent = new Intent(Intent.ACTION_MAIN); fragment = new KeyboardLayoutDialogFragment(inputDeviceIdentifier);
intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class); fragment.setTargetFragment(this, 0);
intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER, fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
inputDeviceIdentifier);
intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_INFO, imi);
intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_SUBTYPE, imSubtype);
startActivity(intent);
}
private void clearLoader() {
for (final int loaderId : mLoaderIDs) {
getLoaderManager().destroyLoader(loaderId);
} }
mLoaderIDs.clear();
} }
private void registerShowVirtualKeyboardSettingsObserver() { private void registerShowVirtualKeyboardSettingsObserver() {
@@ -282,102 +234,75 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
} }
}; };
private static final class Callbacks implements LoaderManager.LoaderCallbacks<List<Keyboards>> { @Override
@NonNull public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
final Context mContext; final Intent intent = new Intent(Intent.ACTION_MAIN);
@NonNull intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
final PhysicalKeyboardFragment mPhysicalKeyboardFragment; intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
@NonNull inputDeviceIdentifier);
final List<HardKeyboardDeviceInfo> mHardKeyboards; mIntentWaitingForResult = intent;
startActivityForResult(intent, 0);
}
public Callbacks( @Override
@NonNull Context context, public void onActivityResult(int requestCode, int resultCode, Intent data) {
@NonNull PhysicalKeyboardFragment physicalKeyboardFragment, super.onActivityResult(requestCode, resultCode, data);
@NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
mContext = context;
mPhysicalKeyboardFragment = physicalKeyboardFragment;
mHardKeyboards = hardKeyboards;
}
@Override if (mIntentWaitingForResult != null) {
public Loader<List<Keyboards>> onCreateLoader(int id, Bundle args) { InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
return new KeyboardLayoutLoader(mContext, mHardKeyboards); .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
} mIntentWaitingForResult = null;
showKeyboardLayoutDialog(inputDeviceIdentifier);
@Override
public void onLoadFinished(Loader<List<Keyboards>> loader, List<Keyboards> data) {
mPhysicalKeyboardFragment.onLoadFinishedInternal(loader.getId(), data);
}
@Override
public void onLoaderReset(Loader<List<Keyboards>> loader) {
} }
} }
private static final class KeyboardLayoutLoader extends AsyncTaskLoader<List<Keyboards>> { private static String getLayoutLabel(@NonNull InputDevice device,
@NonNull @NonNull Context context, @NonNull InputManager im) {
private final List<HardKeyboardDeviceInfo> mHardKeyboards; final String currentLayoutDesc =
im.getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
public KeyboardLayoutLoader( if (currentLayoutDesc == null) {
@NonNull Context context, return context.getString(R.string.keyboard_layout_default_label);
@NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
super(context);
mHardKeyboards = Preconditions.checkNotNull(hardKeyboards);
} }
final KeyboardLayout currentLayout = im.getKeyboardLayout(currentLayoutDesc);
if (currentLayout == null) {
return context.getString(R.string.keyboard_layout_default_label);
}
// If current layout is specified but the layout is null, just return an empty string
// instead of falling back to R.string.keyboard_layout_default_label.
return TextUtils.emptyIfNull(currentLayout.getLabel());
}
private Keyboards loadInBackground(HardKeyboardDeviceInfo deviceInfo) { @NonNull
final ArrayList<Keyboards.KeyboardInfo> keyboardInfoList = new ArrayList<>(); static List<HardKeyboardDeviceInfo> getHardKeyboards(@NonNull Context context) {
final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
final InputManager im = getContext().getSystemService(InputManager.class); final InputManager im = context.getSystemService(InputManager.class);
if (imm != null && im != null) { if (im == null) {
for (InputMethodInfo imi : imm.getEnabledInputMethodList()) { return new ArrayList<>();
final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList( }
imi, true /* allowsImplicitlySelectedSubtypes */); for (int deviceId : InputDevice.getDeviceIds()) {
if (subtypes.isEmpty()) { final InputDevice device = InputDevice.getDevice(deviceId);
// Here we use null to indicate that this IME has no subtype. if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
final InputMethodSubtype nullSubtype = null; continue;
final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
deviceInfo.mDeviceIdentifier, imi, nullSubtype);
keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, nullSubtype, layout));
continue;
}
// If the IME supports subtypes, we pick up "keyboard" subtypes only.
final int N = subtypes.size();
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
if (!IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
continue;
}
final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
deviceInfo.mDeviceIdentifier, imi, subtype);
keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
}
}
} }
return new Keyboards(deviceInfo, keyboardInfoList); keyboards.add(new HardKeyboardDeviceInfo(
device.getName(), device.getIdentifier(), getLayoutLabel(device, context, im)));
} }
@Override // We intentionally don't reuse Comparator because Collator may not be thread-safe.
public List<Keyboards> loadInBackground() { final Collator collator = Collator.getInstance();
List<Keyboards> keyboardsList = new ArrayList<>(mHardKeyboards.size()); keyboards.sort((a, b) -> {
for (HardKeyboardDeviceInfo deviceInfo : mHardKeyboards) { int result = collator.compare(a.mDeviceName, b.mDeviceName);
keyboardsList.add(loadInBackground(deviceInfo)); if (result != 0) {
return result;
} }
return keyboardsList; result = a.mDeviceIdentifier.getDescriptor().compareTo(
} b.mDeviceIdentifier.getDescriptor());
if (result != 0) {
@Override return result;
protected void onStartLoading() { }
super.onStartLoading(); return collator.compare(a.mLayoutLabel, b.mLayoutLabel);
forceLoad(); });
} return keyboards;
@Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
}
} }
public static final class HardKeyboardDeviceInfo { public static final class HardKeyboardDeviceInfo {
@@ -385,12 +310,16 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
public final String mDeviceName; public final String mDeviceName;
@NonNull @NonNull
public final InputDeviceIdentifier mDeviceIdentifier; public final InputDeviceIdentifier mDeviceIdentifier;
@NonNull
public final String mLayoutLabel;
public HardKeyboardDeviceInfo( public HardKeyboardDeviceInfo(
@Nullable final String deviceName, @Nullable String deviceName,
@NonNull final InputDeviceIdentifier deviceIdentifier) { @NonNull InputDeviceIdentifier deviceIdentifier,
mDeviceName = deviceName != null ? deviceName : ""; @NonNull String layoutLabel) {
mDeviceName = TextUtils.emptyIfNull(deviceName);
mDeviceIdentifier = deviceIdentifier; mDeviceIdentifier = deviceIdentifier;
mLayoutLabel = layoutLabel;
} }
@Override @Override
@@ -404,14 +333,10 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
if (!TextUtils.equals(mDeviceName, that.mDeviceName)) { if (!TextUtils.equals(mDeviceName, that.mDeviceName)) {
return false; return false;
} }
if (mDeviceIdentifier.getVendorId() != that.mDeviceIdentifier.getVendorId()) { if (!Objects.equals(mDeviceIdentifier, that.mDeviceIdentifier)) {
return false; return false;
} }
if (mDeviceIdentifier.getProductId() != that.mDeviceIdentifier.getProductId()) { if (!TextUtils.equals(mLayoutLabel, that.mLayoutLabel)) {
return false;
}
if (!TextUtils.equals(mDeviceIdentifier.getDescriptor(),
that.mDeviceIdentifier.getDescriptor())) {
return false; return false;
} }
@@ -419,121 +344,6 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
} }
} }
public static final class Keyboards implements Comparable<Keyboards> {
@NonNull
public final HardKeyboardDeviceInfo mDeviceInfo;
@NonNull
public final ArrayList<KeyboardInfo> mKeyboardInfoList;
@NonNull
public final Collator mCollator = Collator.getInstance();
public Keyboards(
@NonNull final HardKeyboardDeviceInfo deviceInfo,
@NonNull final ArrayList<KeyboardInfo> keyboardInfoList) {
mDeviceInfo = deviceInfo;
mKeyboardInfoList = keyboardInfoList;
}
@Override
public int compareTo(@NonNull Keyboards another) {
return mCollator.compare(mDeviceInfo.mDeviceName, another.mDeviceInfo.mDeviceName);
}
public static final class KeyboardInfo {
@NonNull
public final InputMethodInfo mImi;
@Nullable
public final InputMethodSubtype mImSubtype;
@NonNull
public final KeyboardLayout mLayout;
public KeyboardInfo(
@NonNull final InputMethodInfo imi,
@Nullable final InputMethodSubtype imSubtype,
@NonNull final KeyboardLayout layout) {
mImi = imi;
mImSubtype = imSubtype;
mLayout = layout;
}
}
}
static final class KeyboardInfoPreference extends Preference {
@NonNull
private final CharSequence mImeName;
@Nullable
private final CharSequence mImSubtypeName;
@NonNull
private final Collator collator = Collator.getInstance();
private KeyboardInfoPreference(
@NonNull Context context, @NonNull Keyboards.KeyboardInfo info) {
super(context);
mImeName = info.mImi.loadLabel(context.getPackageManager());
mImSubtypeName = getImSubtypeName(context, info.mImi, info.mImSubtype);
setTitle(formatDisplayName(context, mImeName, mImSubtypeName));
if (info.mLayout != null) {
setSummary(info.mLayout.getLabel());
}
}
@NonNull
static CharSequence getDisplayName(
@NonNull Context context, @NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype imSubtype) {
final CharSequence imeName = imi.loadLabel(context.getPackageManager());
final CharSequence imSubtypeName = getImSubtypeName(context, imi, imSubtype);
return formatDisplayName(context, imeName, imSubtypeName);
}
private static CharSequence formatDisplayName(
@NonNull Context context,
@NonNull CharSequence imeName, @Nullable CharSequence imSubtypeName) {
if (imSubtypeName == null) {
return imeName;
}
return String.format(
context.getString(R.string.physical_device_title), imeName, imSubtypeName);
}
@Nullable
private static CharSequence getImSubtypeName(
@NonNull Context context, @NonNull InputMethodInfo imi,
@Nullable InputMethodSubtype imSubtype) {
if (imSubtype != null) {
return InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
imSubtype, context, imi);
}
return null;
}
@Override
public int compareTo(@NonNull Preference object) {
if (!(object instanceof KeyboardInfoPreference)) {
return super.compareTo(object);
}
KeyboardInfoPreference another = (KeyboardInfoPreference) object;
int result = compare(mImeName, another.mImeName);
if (result == 0) {
result = compare(mImSubtypeName, another.mImSubtypeName);
}
return result;
}
private int compare(@Nullable CharSequence lhs, @Nullable CharSequence rhs) {
if (!TextUtils.isEmpty(lhs) && !TextUtils.isEmpty(rhs)) {
return collator.compare(lhs.toString(), rhs.toString());
} else if (TextUtils.isEmpty(lhs) && TextUtils.isEmpty(rhs)) {
return 0;
} else if (!TextUtils.isEmpty(lhs)) {
return -1;
} else {
return 1;
}
}
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() { new BaseSearchIndexProvider() {
@Override @Override

View File

@@ -94,7 +94,7 @@ public class PhysicalKeyboardPreferenceController extends AbstractPreferenceCont
return; return;
} }
final List<HardKeyboardDeviceInfo> keyboards = final List<HardKeyboardDeviceInfo> keyboards =
PhysicalKeyboardFragment.getHardKeyboards(); PhysicalKeyboardFragment.getHardKeyboards(mContext);
if (keyboards.isEmpty()) { if (keyboards.isEmpty()) {
mPreference.setSummary(R.string.disconnected); mPreference.setSummary(R.string.disconnected);
return; return;

View File

@@ -24,6 +24,7 @@ import android.content.Intent;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.net.Uri; import android.net.Uri;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.provider.SettingsSlicesContract;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.util.Log; import android.util.Log;
@@ -63,6 +64,10 @@ public class SettingsSliceProvider extends SliceProvider {
private static final String TAG = "SettingsSliceProvider"; private static final String TAG = "SettingsSliceProvider";
/**
* Authority for Settings slices not officially supported by the platform, but extensible for
* OEMs.
*/
public static final String SLICE_AUTHORITY = "com.android.settings.slices"; public static final String SLICE_AUTHORITY = "com.android.settings.slices";
public static final String PATH_WIFI = "wifi"; public static final String PATH_WIFI = "wifi";
@@ -82,13 +87,6 @@ public class SettingsSliceProvider extends SliceProvider {
@VisibleForTesting @VisibleForTesting
Map<Uri, SliceData> mSliceDataCache; Map<Uri, SliceData> mSliceDataCache;
public static Uri getUri(String path) {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SLICE_AUTHORITY)
.appendPath(path).build();
}
@Override @Override
public boolean onCreateSliceProvider() { public boolean onCreateSliceProvider() {
mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
@@ -176,7 +174,8 @@ public class SettingsSliceProvider extends SliceProvider {
.setSubtitle(state) .setSubtitle(state)
.addEndItem(new SliceAction(getBroadcastIntent(ACTION_WIFI_CHANGED), .addEndItem(new SliceAction(getBroadcastIntent(ACTION_WIFI_CHANGED),
null, finalWifiEnabled)) null, finalWifiEnabled))
.setPrimaryAction(new SliceAction(getIntent(Intent.ACTION_MAIN), null, null))) .setPrimaryAction(
new SliceAction(getIntent(Intent.ACTION_MAIN), null, null)))
.build(); .build();
} }

View File

@@ -59,7 +59,8 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
// Wait a bit for wifi to update (TODO: is there a better way to do this?) // Wait a bit for wifi to update (TODO: is there a better way to do this?)
Handler h = new Handler(); Handler h = new Handler();
h.postDelayed(() -> { h.postDelayed(() -> {
Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI); Uri uri = SliceBuilderUtils.getUri(SettingsSliceProvider.PATH_WIFI,
false /* isPlatformSlice */);
context.getContentResolver().notifyChange(uri, null); context.getContentResolver().notifyChange(uri, null);
}, 1000); }, 1000);
break; break;

View File

@@ -19,9 +19,12 @@ package com.android.settings.slices;
import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.net.Uri;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -98,6 +101,17 @@ public class SliceBuilderUtils {
sliceData.getKey()); sliceData.getKey());
} }
public static Uri getUri(String path, boolean isPlatformSlice) {
final String authority = isPlatformSlice
? SettingsSlicesContract.AUTHORITY
: SettingsSliceProvider.SLICE_AUTHORITY;
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.appendPath(path)
.build();
}
private static BasePreferenceController getPreferenceController(Context context, private static BasePreferenceController getPreferenceController(Context context,
String controllerClassName, String controllerKey) { String controllerClassName, String controllerKey) {
try { try {

View File

@@ -70,6 +70,8 @@ public class SliceData {
@SliceType @SliceType
private final int mSliceType; private final int mSliceType;
private final boolean mIsPlatformDefined;
public String getKey() { public String getKey() {
return mKey; return mKey;
} }
@@ -106,6 +108,10 @@ public class SliceData {
return mSliceType; return mSliceType;
} }
public boolean isPlatformDefined() {
return mIsPlatformDefined;
}
private SliceData(Builder builder) { private SliceData(Builder builder) {
mKey = builder.mKey; mKey = builder.mKey;
mTitle = builder.mTitle; mTitle = builder.mTitle;
@@ -116,6 +122,7 @@ public class SliceData {
mUri = builder.mUri; mUri = builder.mUri;
mPreferenceController = builder.mPrefControllerClassName; mPreferenceController = builder.mPrefControllerClassName;
mSliceType = builder.mSliceType; mSliceType = builder.mSliceType;
mIsPlatformDefined = builder.mIsPlatformDefined;
} }
@Override @Override
@@ -151,6 +158,8 @@ public class SliceData {
private int mSliceType; private int mSliceType;
private boolean mIsPlatformDefined;
public Builder setKey(String key) { public Builder setKey(String key) {
mKey = key; mKey = key;
return this; return this;
@@ -196,6 +205,11 @@ public class SliceData {
return this; return this;
} }
public Builder setPlatformDefined(boolean isPlatformDefined) {
mIsPlatformDefined = isPlatformDefined;
return this;
}
public SliceData build() { public SliceData build() {
if (TextUtils.isEmpty(mKey)) { if (TextUtils.isEmpty(mKey)) {
throw new IllegalStateException("Key cannot be empty"); throw new IllegalStateException("Key cannot be empty");

View File

@@ -19,6 +19,7 @@ package com.android.settings.slices;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_CONTROLLER;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_ICON; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_ICON;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_PLATFORM_SLICE_FLAG;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_SUMMARY;
import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE; import static com.android.settings.core.PreferenceXmlParserUtils.METADATA_TITLE;
@@ -164,7 +165,8 @@ class SliceDataConverter {
| MetadataFlag.FLAG_NEED_PREF_TYPE | MetadataFlag.FLAG_NEED_PREF_TYPE
| MetadataFlag.FLAG_NEED_PREF_TITLE | MetadataFlag.FLAG_NEED_PREF_TITLE
| MetadataFlag.FLAG_NEED_PREF_ICON | MetadataFlag.FLAG_NEED_PREF_ICON
| MetadataFlag.FLAG_NEED_PREF_SUMMARY); | MetadataFlag.FLAG_NEED_PREF_SUMMARY
| MetadataFlag.FLAG_NEED_PLATFORM_SLICE_FLAG);
for (Bundle bundle : metadata) { for (Bundle bundle : metadata) {
// TODO (b/67996923) Non-controller Slices should become intent-only slices. // TODO (b/67996923) Non-controller Slices should become intent-only slices.
@@ -179,6 +181,7 @@ class SliceDataConverter {
final int iconResId = bundle.getInt(METADATA_ICON); final int iconResId = bundle.getInt(METADATA_ICON);
final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName, final int sliceType = SliceBuilderUtils.getSliceType(mContext, controllerClassName,
key); key);
final boolean isPlatformSlice = bundle.getBoolean(METADATA_PLATFORM_SLICE_FLAG);
final SliceData xmlSlice = new SliceData.Builder() final SliceData xmlSlice = new SliceData.Builder()
.setKey(key) .setKey(key)
@@ -189,6 +192,7 @@ class SliceDataConverter {
.setPreferenceControllerClassName(controllerClassName) .setPreferenceControllerClassName(controllerClassName)
.setFragmentName(fragmentName) .setFragmentName(fragmentName)
.setSliceType(sliceType) .setSliceType(sliceType)
.setPlatformDefined(isPlatformSlice)
.build(); .build();
xmlSliceData.add(xmlSlice); xmlSliceData.add(xmlSlice);

View File

@@ -24,6 +24,7 @@
android:title="title" android:title="title"
android:icon="@drawable/ic_android" android:icon="@drawable/ic_android"
android:summary="summary" android:summary="summary"
settings:controller="com.android.settings.slices.FakePreferenceController"/> settings:controller="com.android.settings.slices.FakePreferenceController"
settings:platform_slice="true"/>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -55,7 +55,7 @@ public class PhysicalKeyboardPreferenceControllerTest {
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.INPUT_SERVICE)).thenReturn(mIm); when(mContext.getSystemService(InputManager.class)).thenReturn(mIm);
mController = new PhysicalKeyboardPreferenceController(mContext, null /* lifecycle */); mController = new PhysicalKeyboardPreferenceController(mContext, null /* lifecycle */);
} }

View File

@@ -18,8 +18,7 @@
package com.android.settings.slices; package com.android.settings.slices;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -28,6 +27,7 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; import android.net.Uri;
import android.provider.SettingsSlicesContract;
import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.FakeToggleController; import com.android.settings.testutils.FakeToggleController;
@@ -50,6 +50,8 @@ import java.util.HashMap;
public class SettingsSliceProviderTest { public class SettingsSliceProviderTest {
private final String KEY = "KEY"; private final String KEY = "KEY";
private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
private final String TITLE = "title"; private final String TITLE = "title";
private final String SUMMARY = "summary"; private final String SUMMARY = "summary";
private final String SCREEN_TITLE = "screen title"; private final String SCREEN_TITLE = "screen title";
@@ -67,6 +69,8 @@ public class SettingsSliceProviderTest {
mProvider = spy(new SettingsSliceProvider()); mProvider = spy(new SettingsSliceProvider());
mProvider.mSliceDataCache = new HashMap<>(); mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext); mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
when(mProvider.getContext()).thenReturn(mContext);
mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase(); mDb = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
SlicesDatabaseHelper.getInstance(mContext).setIndexedState(); SlicesDatabaseHelper.getInstance(mContext).setIndexedState();
} }
@@ -78,34 +82,18 @@ public class SettingsSliceProviderTest {
@Test @Test
public void testInitialSliceReturned_emptySlice() { public void testInitialSliceReturned_emptySlice() {
insertSpecialCase(KEY); insertSpecialCase(INTENT_PATH);
ContentResolver mockResolver = mock(ContentResolver.class); Uri uri = SliceBuilderUtils.getUri(INTENT_PATH, false);
doReturn(mockResolver).when(mContext).getContentResolver();
when(mProvider.getContext()).thenReturn(mContext);
Uri uri = SettingsSliceProvider.getUri(KEY);
Slice slice = mProvider.onBindSlice(uri); Slice slice = mProvider.onBindSlice(uri);
assertThat(slice.getUri()).isEqualTo(uri); assertThat(slice.getUri()).isEqualTo(uri);
assertThat(slice.getItems()).isEmpty(); assertThat(slice.getItems()).isEmpty();
} }
@Test
public void testUriBuilder_returnsValidSliceUri() {
Uri uri = SettingsSliceProvider.getUri(KEY);
assertThat(uri.getScheme()).isEqualTo(ContentResolver.SCHEME_CONTENT);
assertThat(uri.getAuthority()).isEqualTo(SettingsSliceProvider.SLICE_AUTHORITY);
assertThat(uri.getLastPathSegment()).isEqualTo(KEY);
}
@Test @Test
public void testLoadSlice_returnsSliceFromAccessor() { public void testLoadSlice_returnsSliceFromAccessor() {
ContentResolver mockResolver = mock(ContentResolver.class);
doReturn(mockResolver).when(mContext).getContentResolver();
when(mProvider.getContext()).thenReturn(mContext);
insertSpecialCase(KEY); insertSpecialCase(KEY);
Uri uri = SettingsSliceProvider.getUri(KEY); Uri uri = SliceBuilderUtils.getUri(KEY, false);
mProvider.loadSlice(uri); mProvider.loadSlice(uri);
SliceData data = mProvider.mSliceDataCache.get(uri); SliceData data = mProvider.mSliceDataCache.get(uri);
@@ -116,7 +104,6 @@ public class SettingsSliceProviderTest {
@Test @Test
public void testLoadSlice_cachedEntryRemovedOnBuild() { public void testLoadSlice_cachedEntryRemovedOnBuild() {
when(mProvider.getContext()).thenReturn(mContext);
SliceData data = getDummyData(); SliceData data = getDummyData();
mProvider.mSliceDataCache.put(data.getUri(), data); mProvider.mSliceDataCache.put(data.getUri(), data);
mProvider.onBindSlice(data.getUri()); mProvider.onBindSlice(data.getUri());

View File

@@ -20,8 +20,10 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.provider.SettingsSlicesContract;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
@@ -48,6 +50,9 @@ public class SliceBuilderUtilsTest {
private final Class PREF_CONTROLLER = FakeToggleController.class; private final Class PREF_CONTROLLER = FakeToggleController.class;
private final Class PREF_CONTROLLER2 = FakeContextOnlyPreferenceController.class; private final Class PREF_CONTROLLER2 = FakeContextOnlyPreferenceController.class;
private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY;
private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY;
private Context mContext; private Context mContext;
@Before @Before
@@ -62,6 +67,58 @@ public class SliceBuilderUtilsTest {
assertThat(slice).isNotNull(); // TODO improve test for Slice content assertThat(slice).isNotNull(); // TODO improve test for Slice content
} }
@Test
public void testUriBuilder_oemAuthority_intentPath_returnsValidSliceUri() {
Uri expectedUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(INTENT_PATH)
.build();
Uri actualUri = SliceBuilderUtils.getUri(INTENT_PATH, false);
assertThat(actualUri).isEqualTo(expectedUri);
}
@Test
public void testUriBuilder_oemAuthority_actionPath_returnsValidSliceUri() {
Uri expectedUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(ACTION_PATH)
.build();
Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, false);
assertThat(actualUri).isEqualTo(expectedUri);
}
@Test
public void testUriBuilder_platformAuthority_intentPath_returnsValidSliceUri() {
Uri expectedUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSlicesContract.AUTHORITY)
.appendPath(ACTION_PATH)
.build();
Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true);
assertThat(actualUri).isEqualTo(expectedUri);
}
@Test
public void testUriBuilder_platformAuthority_actionPath_returnsValidSliceUri() {
Uri expectedUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSlicesContract.AUTHORITY)
.appendPath(ACTION_PATH)
.build();
Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true);
assertThat(actualUri).isEqualTo(expectedUri);
}
@Test @Test
public void testGetPreferenceController_buildsMatchingController() { public void testGetPreferenceController_buildsMatchingController() {
BasePreferenceController controller = BasePreferenceController controller =

View File

@@ -81,6 +81,7 @@ public class SliceDataConverterTest {
assertThat(fakeSlice.getUri()).isNull(); assertThat(fakeSlice.getUri()).isNull();
assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName); assertThat(fakeSlice.getFragmentClassName()).isEqualTo(fakeFragmentClassName);
assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName); assertThat(fakeSlice.getPreferenceController()).isEqualTo(fakeControllerName);
assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER); assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER); // from XML
assertThat(fakeSlice.isPlatformDefined()).isTrue(); // from XML
} }
} }

View File

@@ -37,6 +37,7 @@ public class SliceDataTest {
private final Uri URI = Uri.parse("content://com.android.settings.slices/test"); private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private final String PREF_CONTROLLER = "com.android.settings.slices.tester"; private final String PREF_CONTROLLER = "com.android.settings.slices.tester";
private final int SLICE_TYPE = SliceData.SliceType.SWITCH; private final int SLICE_TYPE = SliceData.SliceType.SWITCH;
private final boolean IS_PLATFORM_DEFINED = true;
@Test @Test
public void testBuilder_buildsMatchingObject() { public void testBuilder_buildsMatchingObject() {
@@ -49,7 +50,8 @@ public class SliceDataTest {
.setFragmentName(FRAGMENT_NAME) .setFragmentName(FRAGMENT_NAME)
.setUri(URI) .setUri(URI)
.setPreferenceControllerClassName(PREF_CONTROLLER) .setPreferenceControllerClassName(PREF_CONTROLLER)
.setSliceType(SLICE_TYPE); .setSliceType(SLICE_TYPE)
.setPlatformDefined(IS_PLATFORM_DEFINED);
SliceData data = builder.build(); SliceData data = builder.build();
@@ -62,6 +64,7 @@ public class SliceDataTest {
assertThat(data.getUri()).isEqualTo(URI); assertThat(data.getUri()).isEqualTo(URI);
assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
assertThat(data.getSliceType()).isEqualTo(SLICE_TYPE); assertThat(data.getSliceType()).isEqualTo(SLICE_TYPE);
assertThat(data.isPlatformDefined()).isEqualTo(IS_PLATFORM_DEFINED);
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)

View File

@@ -90,7 +90,7 @@ public class SlicesDatabaseAccessorTest {
public void testGetSliceFromUri_validUri_validSliceReturned() { public void testGetSliceFromUri_validUri_validSliceReturned() {
String key = "key"; String key = "key";
insertSpecialCase(key); insertSpecialCase(key);
Uri uri = SettingsSliceProvider.getUri(key); Uri uri = SliceBuilderUtils.getUri(key, false);
SliceData data = mAccessor.getSliceDataFromUri(uri); SliceData data = mAccessor.getSliceDataFromUri(uri);
@@ -106,7 +106,7 @@ public class SlicesDatabaseAccessorTest {
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testGetSliceFromUri_invalidUri_errorThrown() { public void testGetSliceFromUri_invalidUri_errorThrown() {
Uri uri = SettingsSliceProvider.getUri("durr"); Uri uri = SliceBuilderUtils.getUri("durr", false);
mAccessor.getSliceDataFromUri(uri); mAccessor.getSliceDataFromUri(uri);
} }