Adding A feature flag to disable widgets and a corresponding build

target Launcher3Go without widgets support

Bug: 62353159
Change-Id: Ia03d2235a9bcf75f9ef191924f81630e63a2c684
This commit is contained in:
Sunny Goyal 2017-07-03 13:50:52 -07:00
parent 70999fef4b
commit 64a75aa305
22 changed files with 473 additions and 97 deletions

View File

@ -17,7 +17,7 @@
LOCAL_PATH := $(call my-dir)
#
# Build app code.
# Build rule for Launcher3 app.
#
include $(CLEAR_VARS)
@ -62,6 +62,57 @@ LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
include $(BUILD_PACKAGE)
#
# Build rule for Launcher3 Go app for Android Go devices.
#
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-recyclerview \
android-support-v7-palette \
android-support-dynamic-animation
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_config) \
$(call all-java-files-under, go/src_flags) \
$(call all-proto-files-under, protos) \
$(call all-proto-files-under, proto_overrides)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/go/res \
$(LOCAL_PATH)/res \
prebuilts/sdk/current/support/v7/recyclerview/res \
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.v7.recyclerview \
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 21
LOCAL_PACKAGE_NAME := Launcher3Go
LOCAL_PRIVILEGED_MODULE := true
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := go/AndroidManifest.xml
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
include $(BUILD_PACKAGE)
#
# Launcher proto buffer jar used for development
#

View File

@ -35,11 +35,16 @@ android {
applicationId 'com.android.launcher3'
testApplicationId 'com.android.launcher3.tests'
}
l3go {
applicationId 'com.android.launcher3'
testApplicationId 'com.android.launcher3.tests'
}
}
sourceSets {
main {
res.srcDirs = ['res']
java.srcDirs = ['src', 'src_flags']
java.srcDirs = ['src']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
srcDir 'protos/'
@ -48,18 +53,31 @@ android {
}
androidTest {
java.srcDirs = ['tests/src']
res.srcDirs = ['tests/res']
java.srcDirs = ['tests/src']
manifest.srcFile "tests/AndroidManifest-common.xml"
}
aosp {
java.srcDirs = ['src_flags']
manifest.srcFile "AndroidManifest.xml"
}
aospAndroidTest {
manifest.srcFile "tests/AndroidManifest.xml"
}
l3go {
res.srcDirs = ['go/res']
java.srcDirs = ['go/src_flags']
// Note: we are using the Launcher3 manifest here because the gradle manifest-merger uses
// different attributes than the build system.
manifest.srcFile "AndroidManifest.xml"
}
l3goAndroidTest {
manifest.srcFile "tests/AndroidManifest.xml"
}
}
}
@ -74,10 +92,10 @@ dependencies {
compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
testCompile 'junit:junit:4.12'
androidTestCompile "org.mockito:mockito-core:1.+"
androidTestCompile "org.mockito:mockito-core:1.9.5"
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
androidTestCompile 'com.android.support.test:runner:0.5'

53
go/AndroidManifest.xml Normal file
View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2017, 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.
*/
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
android:fullBackupContent="@xml/backupscheme"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher_home"
android:label="@string/derived_app_name"
android:theme="@style/LauncherTheme"
android:largeHeap="@bool/config_largeHeap"
android:restoreAnyVersion="true"
android:supportsRtl="true" >
<!-- Activity for handling PinItemRequest. Only supports shortcuts -->
<activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
android:label="@string/action_add_to_workspace"
tools:merge="override" >
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/widget_preview_label_vertical_padding"
android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
android:orientation="horizontal">
<!-- The name of the widget. -->
<TextView
android:id="@+id/widget_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:fadingEdge="horizontal"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:singleLine="true"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
<!-- The original dimensions of the widget (can't be the same text as above due to different
style. -->
<TextView
android:id="@+id/widget_dims"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:visibility="gone"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:fontFamily="sans-serif-condensed"
android:alpha="0.8" />
</LinearLayout>
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</merge>

33
go/res/values/strings.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2017 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.
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
<string name="long_press_widget_to_add">Touch &amp; hold to pick up a shortcut.</string>
<!-- Accessibility spoken hint message in widget picker, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
<string name="long_accessible_way_to_add">Double-tap &amp; hold to pick up a shortcut or use custom actions.</string>
<!-- Text for shortcut add button -->
<string name="widget_button_text">Shortcuts</string>
<!-- Strings for widgets & more in the popup container/bottom sheet -->
<!-- Title for a bottom sheet that shows shortcuts for a particular app -->
<string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> shortcuts</string>
</resources>

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 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.config;
/**
* Defines a set of flags used to control various launcher behaviors
*/
public final class FeatureFlags {
public static final boolean IS_DOGFOOD_BUILD = false;
private FeatureFlags() {}
// Custom flags go below this
public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled while all-apps open, the soft input will be set to adjust resize .
public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = true;
// When enabled the promise icon is visible in all apps while installation an app.
public static boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
// When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
public static boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
// When enabled allows use of physics based motions in the Launcher.
public static boolean LAUNCHER3_PHYSICS = true;
// When enabled allows use of spring motions on the icons.
public static boolean LAUNCHER3_SPRING_ICONS = true;
// Feature flag to enable moving the QSB on the 0th screen of the workspace.
public static final boolean QSB_ON_FIRST_SCREEN = true;
// When enabled the all-apps icon is not added to the hotseat.
public static final boolean NO_ALL_APPS_ICON = true;
// When enabled fling down gesture on the first workspace triggers search.
public static final boolean PULLDOWN_SEARCH = false;
// When enabled the status bar may show dark icons based on the top of the wallpaper.
public static final boolean LIGHT_STATUS_BAR = false;
// When enabled icons are badged with the number of notifications associated with that app.
public static final boolean BADGE_ICONS = true;
// When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
public static final boolean LEGACY_ICON_TREATMENT = true;
// When enabled, adaptive icons would have shadows baked when being stored to icon cache.
public static final boolean ADAPTIVE_ICON_SHADOW = true;
// When enabled, app discovery will be enabled if service is implemented
public static final boolean DISCOVERY_ENABLED = false;
// When enabled, the qsb will be moved to the hotseat.
public static final boolean QSB_IN_HOTSEAT = true;
// Features to control Launcher3Go behavior
public static final boolean GO_DISABLE_WIDGETS = true;
}

View File

@ -13,6 +13,7 @@ import android.support.annotation.WorkerThread;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
@ -26,7 +27,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0);
Log.d(TAG, "Widget ID map received for host:" + hostId);
if (hostId != Launcher.APPWIDGET_HOST_ID) {
if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) {
return;
}
@ -38,7 +39,8 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
restoreAppWidgetIds(context, asyncResult, oldIds, newIds);
restoreAppWidgetIds(context, oldIds, newIds);
asyncResult.finish();
}
});
} else {
@ -51,9 +53,13 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
static void restoreAppWidgetIds(Context context, PendingResult asyncResult,
int[] oldWidgetIds, int[] newWidgetIds) {
AppWidgetHost appWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
if (FeatureFlags.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
appWidgetHost.deleteHost();
return;
}
if (!RestoreDbTask.isPending(context)) {
// Someone has already gone through our DB once, probably LoaderTask. Skip any further
// modifications of the DB.
@ -62,7 +68,6 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
Log.d(TAG, "Deleting widgetId: " + widgetId);
appWidgetHost.deleteAppWidgetId(widgetId);
}
asyncResult.finish();
return;
}
final ContentResolver cr = context.getContentResolver();
@ -106,6 +111,5 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
if (app != null) {
app.getModel().forceReload();
}
asyncResult.finish();
}
}

View File

@ -19,6 +19,7 @@ package com.android.launcher3;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.logging.UserEventDispatcher;
@ -63,4 +64,9 @@ public abstract class BaseActivity extends Activity {
}
return mSystemUiController;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@ -209,10 +209,8 @@ public class Launcher extends BaseActivity
private boolean mIsSafeModeEnabled;
public static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
private static final int ACTIVITY_START_DELAY = 1000;
// How long to wait before the new-shortcut animation automatically pans the workspace
private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
@ -397,7 +395,10 @@ public class Launcher extends BaseActivity
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost = new LauncherAppWidgetHost(this);
if (Utilities.ATLEAST_MARSHMALLOW) {
mAppWidgetHost.addProviderChangeListener(this);
}
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
@ -788,7 +789,7 @@ public class Launcher extends BaseActivity
}
@Override
protected void onActivityResult(
public void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
handleActivityResult(requestCode, resultCode, data);
if (mLauncherCallbacks != null) {

View File

@ -16,12 +16,20 @@
package com.android.launcher3;
import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.widget.Toast;
import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
@ -33,14 +41,16 @@ import java.util.ArrayList;
*/
public class LauncherAppWidgetHost extends AppWidgetHost {
private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
public static final int APPWIDGET_HOST_ID = 1024;
private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
private Launcher mLauncher;
private final Context mContext;
public LauncherAppWidgetHost(Launcher launcher, int hostId) {
super(launcher, hostId);
mLauncher = launcher;
public LauncherAppWidgetHost(Context context) {
super(context, APPWIDGET_HOST_ID);
mContext = context;
}
@Override
@ -53,6 +63,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
public void startListening() {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return;
}
try {
super.startListening();
} catch (Exception e) {
@ -66,24 +80,38 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
}
public void addProviderChangeListener(Runnable callback) {
@Override
public void stopListening() {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return;
}
super.stopListening();
}
@Override
public int allocateAppWidgetId() {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return AppWidgetManager.INVALID_APPWIDGET_ID;
}
return super.allocateAppWidgetId();
}
public void addProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.add(callback);
}
public void removeProviderChangeListener(Runnable callback) {
public void removeProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.remove(callback);
}
protected void onProvidersChanged() {
if (!mProviderChangeListeners.isEmpty()) {
for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) {
callback.run();
for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) {
callback.notifyWidgetProvidersChanged();
}
}
if (Utilities.ATLEAST_MARSHMALLOW) {
mLauncher.notifyWidgetProvidersChanged();
}
}
public AppWidgetHostView createView(Context context, int appWidgetId,
@ -109,7 +137,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
// will update.
LauncherAppWidgetHostView view = mViews.get(appWidgetId);
if (view == null) {
view = onCreateView(mLauncher, appWidgetId, appWidget);
view = onCreateView(mContext, appWidgetId, appWidget);
}
view.setAppWidget(appWidgetId, appWidget);
view.switchToErrorView();
@ -124,11 +152,11 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
mLauncher, appWidget);
mContext, appWidget);
super.onProviderChanged(appWidgetId, info);
// The super method updates the dimensions of the providerInfo. Update the
// launcher spans accordingly.
info.initSpans(mLauncher);
info.initSpans(mContext);
}
@Override
@ -142,4 +170,53 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
super.clearViews();
mViews.clear();
}
public void startBindFlow(BaseActivity activity,
int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
sendActionCancelled(activity, requestCode);
return;
}
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
activity.startActivityForResult(intent, requestCode);
}
public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
sendActionCancelled(activity, requestCode);
return;
}
try {
startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
} catch (ActivityNotFoundException | SecurityException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
sendActionCancelled(activity, requestCode);
}
}
private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
new Handler().post(new Runnable() {
@Override
public void run() {
activity.onActivityResult(requestCode, Activity.RESULT_CANCELED, null);
}
});
}
/**
* Listener for getting notifications on provider changes.
*/
public interface ProviderChangedListener {
void notifyWidgetProvidersChanged();
}
}

View File

@ -133,7 +133,7 @@ public class LauncherModel extends BroadcastReceiver
}
};
public interface Callbacks {
public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
@ -159,7 +159,6 @@ public class LauncherModel extends BroadcastReceiver
HashSet<String> packageNames, HashSet<ComponentName> components,
UserHandle user);
public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
public void notifyWidgetProvidersChanged();
public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);

View File

@ -1031,7 +1031,7 @@ public class LauncherProvider extends ContentProvider {
}
public AppWidgetHost newLauncherWidgetHost() {
return new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID);
return new LauncherAppWidgetHost(mContext);
}
@Override

View File

@ -52,8 +52,10 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Toast;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
@ -86,6 +88,7 @@ import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@ -4079,7 +4082,7 @@ public class Workspace extends PagedView
* Used as a workaround to ensure that the AppWidgetService receives the
* PACKAGE_ADDED broadcast before updating widgets.
*/
private class DeferredWidgetRefresh implements Runnable {
private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
private final ArrayList<LauncherAppWidgetInfo> mInfos;
private final LauncherAppWidgetHost mHost;
private final Handler mHandler;
@ -4122,6 +4125,11 @@ public class Workspace extends PagedView
}
});
}
@Override
public void notifyWidgetProvidersChanged() {
run();
}
}
private class StateTransitionListener extends AnimatorListenerAdapter

View File

@ -16,15 +16,12 @@
package com.android.launcher3.compat;
import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@ -76,9 +73,6 @@ public abstract class AppWidgetManagerCompat {
public abstract boolean bindAppWidgetIdIfAllowed(
int appWidgetId, AppWidgetProviderInfo info, Bundle options);
public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
Activity activity, AppWidgetHost host, int requestCode);
public abstract LauncherAppWidgetProviderInfo findProvider(
ComponentName provider, UserHandle user);

View File

@ -16,24 +16,21 @@
package com.android.launcher3.compat;
import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.annotation.Nullable;
import android.widget.Toast;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -49,6 +46,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return Collections.emptyList();
}
if (packageUser == null) {
ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
for (UserHandle user : mUserManager.getUserProfiles()) {
@ -71,24 +71,20 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
Bundle options) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return false;
}
return mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.getProfile(), info.provider, options);
}
@Override
public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
AppWidgetHost host, int requestCode) {
try {
host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
} catch (ActivityNotFoundException | SecurityException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
}
}
@Override
public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) {
for (AppWidgetProviderInfo info : mAppWidgetManager
.getInstalledProvidersForProfile(user)) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return null;
}
for (AppWidgetProviderInfo info :
getAllProviders(new PackageUserKey(provider.getPackageName(), user))) {
if (info.provider.equals(provider)) {
return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
@ -99,6 +95,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
@Override
public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return result;
}
for (UserHandle user : mUserManager.getUserProfiles()) {
for (AppWidgetProviderInfo info :
mAppWidgetManager.getInstalledProvidersForProfile(user)) {

View File

@ -20,8 +20,10 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.support.annotation.Nullable;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageUserKey;
import java.util.Collections;
import java.util.List;
class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
@ -32,6 +34,9 @@ class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
@Override
public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
if (FeatureFlags.GO_DISABLE_WIDGETS) {
return Collections.emptyList();
}
if (packageUser == null) {
return super.getAllProviders(null);
}

View File

@ -16,14 +16,8 @@
package com.android.launcher3.dragndrop;
import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ClipData;
import android.content.ClipDescription;
@ -45,8 +39,8 @@ import android.view.View.OnTouchListener;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@ -61,6 +55,11 @@ import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
@ -78,7 +77,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
private LivePreviewWidgetCell mWidgetCell;
// Widget request specific options.
private AppWidgetHost mAppWidgetHost;
private LauncherAppWidgetHost mAppWidgetHost;
private AppWidgetManagerCompat mAppWidgetManager;
private PendingAddWidgetInfo mPendingWidgetInfo;
private int mPendingBindWidgetId;
@ -212,7 +211,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest));
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new AppWidgetHost(this, Launcher.APPWIDGET_HOST_ID);
mAppWidgetHost = new LauncherAppWidgetHost(this);
mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
@ -256,13 +255,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
}
// request bind widget
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
mPendingWidgetInfo.componentName);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
mRequest.getAppWidgetProviderInfo(this).getProfile());
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
}
private void acceptWidget(int widgetId) {
@ -280,7 +274,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BIND_APPWIDGET) {
int widgetId = data != null
? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId)

View File

@ -548,6 +548,11 @@ public class LoaderTask implements Runnable {
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
if (FeatureFlags.GO_DISABLE_WIDGETS) {
c.markDeleted("Only legacy shortcuts can have null package");
continue;
}
// Follow through
case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
// Read all Launcher-specific widget details
boolean customWidget = c.itemType ==

View File

@ -39,12 +39,14 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
/**
* 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)}.
*
* Note: AppWidgetManagerCompat can be disabled using FeatureFlags. In QSB, we should use
* AppWidgetManager directly, so that it keeps working in that case.
*/
public class QsbContainerView extends FrameLayout {
@ -106,7 +108,7 @@ public class QsbContainerView extends FrameLayout {
return QsbWidgetHostView.getDefaultView(container);
}
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(activity);
AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
Bundle opts = new Bundle();
@ -129,7 +131,8 @@ public class QsbContainerView extends FrameLayout {
}
widgetId = mQsbWidgetHost.allocateAppWidgetId();
isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(
widgetId, mWidgetInfo.getProfile(), mWidgetInfo.provider, opts);
if (!isWidgetBound) {
mQsbWidgetHost.deleteAppWidgetId(widgetId);
widgetId = -1;

View File

@ -15,10 +15,8 @@
*/
package com.android.launcher3.widget;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
@ -26,7 +24,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.util.PendingRequestArgs;
/**
@ -56,15 +53,8 @@ public class WidgetAddFlowHandler implements Parcelable {
public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mProviderInfo.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
mProviderInfo.getProfile());
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
launcher.startActivityForResult(intent, requestCode);
launcher.getAppWidgetHost()
.startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
}
/**
@ -85,9 +75,7 @@ public class WidgetAddFlowHandler implements Parcelable {
return false;
}
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
AppWidgetManagerCompat.getInstance(launcher).startConfigActivity(
mProviderInfo, appWidgetId, launcher, launcher.getAppWidgetHost(), requestCode);
launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
return true;
}

View File

@ -21,14 +21,13 @@ package com.android.launcher3.config;
*/
public final class FeatureFlags {
public static final boolean IS_DOGFOOD_BUILD = true;
public static final boolean IS_DOGFOOD_BUILD = false;
private FeatureFlags() {}
// Custom flags go below this
public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
@ -63,4 +62,8 @@ public final class FeatureFlags {
public static final boolean DISCOVERY_ENABLED = false;
// When enabled, the qsb will be moved to the hotseat.
public static final boolean QSB_IN_HOTSEAT = true;
// Features to control Launcher3Go behavior
public static final boolean GO_DISABLE_WIDGETS = false;
}

View File

@ -28,6 +28,7 @@ import android.support.test.uiautomator.UiSelector;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@ -285,7 +286,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase {
pendingInfo.minSpanY = item.minSpanY;
Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
AppWidgetHost host = new AppWidgetHost(mTargetContext, Launcher.APPWIDGET_HOST_ID);
AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
int widgetId = host.allocateAppWidgetId();
if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
host.deleteAppWidgetId(widgetId);