Moving the QSB to the workspace grid.

The QSB will only be resent on the first screen of the workspace
covering the full width of the first row. If will not be movable.
The first screen of the workspace will not be movable.
The searchDropTargetBar no longer contains the QSB (it can be
renamed in aseparate cl).

Refactoring all QSB related logic by moving it to a custom view
inflated only using xml.
Change-Id: Icb4fd6eb855df1af15f685961c38351bf4fd4f4a
This commit is contained in:
Sunny Goyal
2016-05-26 16:05:17 -07:00
parent c54c701a94
commit da4fe1a624
20 changed files with 577 additions and 367 deletions
@@ -0,0 +1,271 @@
/*
* 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.launcher3;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.launcher3.compat.AppWidgetManagerCompat;
/**
* A frame layout which contains a QSB. This internally uses fragment to bind the view, which
* allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
*/
public class QsbContainerView extends FrameLayout {
private boolean mBound;
public QsbContainerView(Context context) {
super(context);
}
public QsbContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public QsbContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!mBound) {
FragmentManager fm = ((Launcher) getContext()).getFragmentManager();
fm.beginTransaction().add(R.id.qsb_container, new QsbFragment()).commit();
mBound = true;
}
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(0, 0, 0, 0);
}
/**
* A fragment to display the QSB.
*/
public static class QsbFragment extends Fragment implements View.OnClickListener {
private static final int REQUEST_BIND_QSB = 1;
private static final String QSB_WIDGET_ID = "qsb_widget_id";
private static int sSavedWidgetId = -1;
private AppWidgetProviderInfo mWidgetInfo;
private LauncherAppWidgetHostView mQsb;
private BroadcastReceiver mRebindReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
rebindFragment();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter(Launcher.ACTION_APPWIDGET_HOST_RESET);
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
getContext().registerReceiver(mRebindReceiver, filter);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (savedInstanceState != null) {
sSavedWidgetId = savedInstanceState.getInt(QSB_WIDGET_ID, -1);
}
Launcher launcher = (Launcher) getActivity();
mWidgetInfo = getSearchWidgetProvider(launcher);
if (mWidgetInfo == null) {
// There is no search provider, just show the default widget.
return getDefaultView(inflater, container, false);
}
SharedPreferences prefs = Utilities.getPrefs(launcher);
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher);
LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost();
InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
Bundle opts = new Bundle();
Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);
AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
boolean isWidgetBound = (widgetInfo != null) &&
widgetInfo.provider.equals(mWidgetInfo.provider);
if (!isWidgetBound) {
// widgetId is already bound and its not the correct provider.
// Delete the widget id.
if (widgetId > -1) {
widgetHost.deleteAppWidgetId(widgetId);
widgetId = -1;
}
widgetId = widgetHost.allocateAppWidgetId();
isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
if (!isWidgetBound) {
widgetHost.deleteAppWidgetId(widgetId);
widgetId = -1;
}
}
if (isWidgetBound) {
mQsb = (LauncherAppWidgetHostView)
widgetHost.createView(launcher, widgetId, mWidgetInfo);
mQsb.setId(R.id.qsb_widget);
mQsb.mErrorViewId = R.layout.qsb_default_view;
if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher)
.getAppWidgetOptions(widgetId), opts)) {
mQsb.updateAppWidgetOptions(opts);
}
mQsb.setPadding(0, 0, 0, 0);
return mQsb;
}
// Return a default widget with setup icon.
return getDefaultView(inflater, container, true);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_qsb_search) {
getActivity().startSearch("", false, null, true);
} else if (view.getId() == R.id.btn_qsb_setup) {
// Allocate a new widget id for QSB
sSavedWidgetId = ((Launcher) getActivity())
.getAppWidgetHost().allocateAppWidgetId();
// Start intent for bind the widget
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, sSavedWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
startActivityForResult(intent, REQUEST_BIND_QSB);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(QSB_WIDGET_ID, sSavedWidgetId);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BIND_QSB) {
if (resultCode == Activity.RESULT_OK) {
int widgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
sSavedWidgetId);
Utilities.getPrefs(getContext()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
sSavedWidgetId = -1;
rebindFragment();
} else if (sSavedWidgetId != -1) {
((Launcher) getActivity()).getAppWidgetHost().deleteAppWidgetId(sSavedWidgetId);
sSavedWidgetId = -1;
}
}
}
@Override
public void onResume() {
super.onResume();
if (mQsb != null && mQsb.isReinflateRequired()) {
rebindFragment();
}
}
@Override
public void onDestroy() {
getContext().unregisterReceiver(mRebindReceiver);
super.onDestroy();
}
private void rebindFragment() {
if (getActivity() != null) {
// Recreate the fragment. This will cause the qsb to be inflated again.
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.qsb_container, new QsbFragment()).commit();
}
}
private View getDefaultView(LayoutInflater inflater, ViewGroup parent, boolean showSetup) {
View v = inflater.inflate(R.layout.qsb_default_view, parent, false);
if (showSetup) {
View setupButton = v.findViewById(R.id.btn_qsb_setup);
setupButton.setVisibility(View.VISIBLE);
setupButton.setOnClickListener(this);
}
v.findViewById(R.id.btn_qsb_search).setOnClickListener(this);
return v;
}
}
/**
* Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
* provided by the same package which is set to be global search activity.
* If widgetCategory is not supported, or no such widget is found, returns the first widget
* provided by the package.
*/
public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
SearchManager searchManager =
(SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
ComponentName searchComponent = searchManager.getGlobalSearchActivity();
if (searchComponent == null) return null;
String providerPkg = searchComponent.getPackageName();
AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
return info;
} else if (defaultWidgetForSearchPackage == null) {
defaultWidgetForSearchPackage = info;
}
}
}
return defaultWidgetForSearchPackage;
}
}