Adding support for prefenrece search in QuickStep

Bug: 62292864
Change-Id: Ic112626ca9c5942c91ced4ab42e64cbce4657701
This commit is contained in:
Sunny Goyal
2017-11-02 18:55:44 -07:00
parent 016d7e93c1
commit 3fbca15555
8 changed files with 380 additions and 1 deletions

View File

@@ -48,6 +48,19 @@
It is set to true so that the activity can be started from command line -->
<activity android:name="com.android.quickstep.RecentsActivity"
android:exported="true" />
<!-- Content provider to settings search -->
<provider
android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
android:authorities="com.android.launcher3"
android:grantUriPermissions="true"
android:multiprocess="true"
android:permission="android.permission.READ_SEARCH_INDEXABLES"
android:exported="true">
<intent-filter>
<action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 Google Inc.
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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:key="pref_add_icon_to_home"
android:title="@string/auto_add_shortcuts_label"
android:summary="@string/auto_add_shortcuts_description"
android:defaultValue="true"
/>
<ListPreference
android:key="pref_override_icon_shape"
android:title="@string/icon_shape_override_label"
android:summary="@string/icon_shape_override_label_location"
android:entries="@array/icon_shape_override_paths_names"
android:entryValues="@array/icon_shape_override_paths_values"
android:defaultValue=""
android:persistent="false" />
</PreferenceScreen>

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2018 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.quickstep;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.Build;
import android.provider.SearchIndexablesContract.XmlResource;
import android.provider.SearchIndexablesProvider;
import android.util.Xml;
import com.android.launcher3.R;
import com.android.launcher3.graphics.IconShapeOverride;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
@TargetApi(Build.VERSION_CODES.O)
public class LauncherSearchIndexablesProvider extends SearchIndexablesProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor queryXmlResources(String[] strings) {
MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
ResolveInfo settingsActivity = getContext().getPackageManager().resolveActivity(
new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.setPackage(getContext().getPackageName()), 0);
cursor.newRow()
.add(XmlResource.COLUMN_XML_RESID, R.xml.indexable_launcher_prefs)
.add(XmlResource.COLUMN_INTENT_ACTION, Intent.ACTION_APPLICATION_PREFERENCES)
.add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, getContext().getPackageName())
.add(XmlResource.COLUMN_INTENT_TARGET_CLASS, settingsActivity.activityInfo.name);
return cursor;
}
@Override
public Cursor queryRawData(String[] projection) {
return new MatrixCursor(INDEXABLES_RAW_COLUMNS);
}
@Override
public Cursor queryNonIndexableKeys(String[] projection) {
MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
// We are not the current launcher. Hide all preferences
try (XmlResourceParser parser = getContext().getResources()
.getXml(R.xml.indexable_launcher_prefs)) {
final int depth = parser.getDepth();
final int[] attrs = new int[] { android.R.attr.key };
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
TypedArray a = getContext().obtainStyledAttributes(
Xml.asAttributeSet(parser), attrs);
cursor.addRow(new String[] {a.getString(0)});
a.recycle();
}
}
} catch (IOException |XmlPullParserException e) {
throw new RuntimeException(e);
}
} else if (!IconShapeOverride.isSupported(getContext())) {
cursor.addRow(new String[] {IconShapeOverride.KEY_PREFERENCE});
}
return cursor;
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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.
-->
<com.android.launcher3.views.HighlightableListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@android:color/transparent"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
android:orientation="vertical"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:scrollbarStyle="outsideOverlay" />

View File

@@ -119,6 +119,10 @@
<!-- Tag id used for view scrim -->
<item type="id" name="view_scrim" />
<!-- View IDs to store item highlight information -->
<item type="id" name="view_unhighlight_background" />
<item type="id" name="view_highlighted" />
<!-- Popup items -->
<integer name="config_popupOpenCloseDuration">150</integer>
<integer name="config_popupArrowOpenDuration">80</integer>

View File

@@ -195,6 +195,8 @@
<!-- Developer setting to change the shape of icons on home screen. [CHAR LIMIT=50] -->
<string name="icon_shape_override_label">Change icon shape</string>
<!-- Subtext explaining that the icons will only be affected on the home screen. This text follows the actual icon action: Change icon shape, on Home screen [CHAR LIMIT=100] -->
<string name="icon_shape_override_label_location">on Home screen</string>
<!-- Option to not change the icon shape on home screen and use the system default setting instead. [CHAR LIMIT=50] -->
<string name="icon_shape_system_default">Use system default</string>
<!-- Option to change the shape of the home screen icons to a square. [CHAR LIMIT=50] -->

View File

@@ -31,11 +31,17 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.views.ButtonPreference;
import com.android.launcher3.views.HighlightableListView;
/**
* Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -48,6 +54,10 @@ public class SettingsActivity extends Activity {
/** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -55,11 +65,15 @@ public class SettingsActivity extends Activity {
if (savedInstanceState == null) {
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new LauncherSettingsFragment())
.replace(android.R.id.content, getNewFragment())
.commit();
}
}
protected PreferenceFragment getNewFragment() {
return new LauncherSettingsFragment();
}
/**
* This fragment shows the launcher preferences.
*/
@@ -67,9 +81,22 @@ public class SettingsActivity extends Activity {
private IconBadgingObserver mIconBadgingObserver;
private String mPreferenceKey;
private boolean mPreferenceHighlighted = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.launcher_preference, container, false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
}
getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
addPreferencesFromResource(R.xml.launcher_preferences);
@@ -100,6 +127,43 @@ public class SettingsActivity extends Activity {
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
}
@Override
public void onResume() {
super.onResume();
Intent intent = getActivity().getIntent();
mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
}
}
private void highlightPreference() {
HighlightableListView list = getView().findViewById(android.R.id.list);
Preference pref = findPreference(mPreferenceKey);
Adapter adapter = list.getAdapter();
if (adapter == null) {
return;
}
// Find the position
int position = -1;
for (int i = adapter.getCount() - 1; i >= 0; i--) {
if (pref == adapter.getItem(i)) {
position = i;
break;
}
}
list.highlightPosition(position);
mPreferenceHighlighted = true;
}
@Override
public void onDestroy() {
if (mIconBadgingObserver != null) {

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2018 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.launcher3.views;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
import java.util.ArrayList;
/**
* Extension of list view with support for element highlighting.
*/
public class HighlightableListView extends ListView {
private int mPosHighlight = -1;
private boolean mColorAnimated = false;
public HighlightableListView(Context context) {
super(context);
}
public HighlightableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HighlightableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(new HighLightAdapter(adapter));
}
public void highlightPosition(int pos) {
if (mPosHighlight == pos) {
return;
}
mColorAnimated = false;
mPosHighlight = pos;
setSelection(mPosHighlight);
int start = getFirstVisiblePosition();
int end = getLastVisiblePosition();
if (start <= mPosHighlight && mPosHighlight <= end) {
highlightView(getChildAt(mPosHighlight - start));
}
}
private void highlightView(View view) {
if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
// already highlighted
} else {
view.setTag(R.id.view_highlighted, true);
view.setTag(R.id.view_unhighlight_background, view.getBackground());
view.setBackground(getHighlightBackground());
view.postDelayed(() -> {
mPosHighlight = -1;
unhighlightView(view);
}, 15000L);
}
}
private void unhighlightView(View view) {
if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
Object background = view.getTag(R.id.view_unhighlight_background);
if (background instanceof Drawable) {
view.setBackground((Drawable) background);
}
view.setTag(R.id.view_unhighlight_background, null);
view.setTag(R.id.view_highlighted, false);
}
}
private class HighLightAdapter extends HeaderViewListAdapter {
public HighLightAdapter(ListAdapter adapter) {
super(new ArrayList<>(), new ArrayList<>(), adapter);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if (position == mPosHighlight) {
highlightView(view);
} else {
unhighlightView(view);
}
return view;
}
}
private ColorDrawable getHighlightBackground() {
int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(getContext()), 26);
if (mColorAnimated) {
return new ColorDrawable(color);
}
mColorAnimated = true;
ColorDrawable bg = new ColorDrawable(Color.WHITE);
ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
anim.setEvaluator(new ArgbEvaluator());
anim.setDuration(200L);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setRepeatCount(4);
anim.start();
return bg;
}
}