Add the Share App system shortcut to the Go Launcher

This shortcut allows users to initiate peer-to-peer app sharing from an app's long-press menu on the home screen. It does so by passing an intent to the specified component. In this base implementation, that specification in config.xml is left blank, and the button will not appear. In GMS Launchers such as LauncherGoGoogle, the config should be set to point to Nearby Sharing.

This also establishes Launcher3QuickStepGo as a distinct subclass of QuickstepLauncher instead of merely a build configuration.

Bug: 157515296
Bug: 174286192
Test: Manual (tested on local wembley devices)
Test: m -j RunLauncherGoGoogleRoboTests
Change-Id: I826bd03721d2e7e963b15d5958e9a18a1cc16f1d
This commit is contained in:
Jon Spivack 2020-11-24 20:24:47 -08:00
parent 20decea517
commit a89a5084f2
7 changed files with 273 additions and 3 deletions

View File

@ -208,11 +208,13 @@ LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
$(call all-java-files-under, go/src)
$(call all-java-files-under, go/src) \
$(call all-java-files-under, go/quickstep/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
$(LOCAL_PATH)/go/res
$(LOCAL_PATH)/go/res \
$(LOCAL_PATH)/go/quickstep/res
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PROGUARD_ENABLED := full
@ -225,7 +227,7 @@ LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
$(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2020, 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"
package="com.android.launcher3">
<uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
-->
<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/AppTheme"
android:largeHeap="@bool/config_largeHeap"
android:restoreAnyVersion="true"
android:supportsRtl="true" >
<!--
Main launcher activity. When extending only change the name, and keep all the
attributes and intent filters the same
-->
<activity
android:name="com.android.launcher3.Launcher3QuickStepGo"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
<meta-data
android:name="com.android.launcher3.grid.control"
android:value="${packageName}.grid_control" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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>
<string name="app_sharing_component" translatable="false"/>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Label for app share drop target. [CHAR_LIMIT=20] -->
<string name="app_share_drop_target_label">Share App</string>
</resources>

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2020 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.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import androidx.core.content.FileProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import java.io.File;
/**
* Defines the Share system shortcut and its factory.
* This shortcut can be added to the app long-press menu on the home screen.
* Clicking the button will initiate peer-to-peer sharing of the app.
*/
public final class AppSharing {
/**
* This flag enables this feature. It is defined here rather than in launcher3's FeatureFlags
* because it is unique to Go and not toggleable at runtime.
*/
public static final boolean ENABLE_APP_SHARING = true;
private static final String TAG = "AppSharing";
private static final String FILE_PROVIDER_SUFFIX = ".overview.fileprovider";
private static final String APP_EXSTENSION = ".apk";
private static final String APP_MIME_TYPE = "application/application";
private final String mSharingComponent;
private AppSharing(Launcher launcher) {
mSharingComponent = launcher.getText(R.string.app_sharing_component).toString();
}
private boolean canShare(ItemInfo info) {
/**
* TODO: Implement once b/168831749 has been resolved
* The implementation should check the validity of the app.
* It should also check whether the app is free or paid, returning false in the latter case.
* For now, all checks occur in the sharing app.
* So, we simply check whether the sharing app is defined.
*/
return !TextUtils.isEmpty(mSharingComponent);
}
private Uri getShareableUri(Context context, String path, String displayName) {
String authority = BuildConfig.APPLICATION_ID + FILE_PROVIDER_SUFFIX;
File pathFile = new File(path);
return FileProvider.getUriForFile(context, authority, pathFile, displayName);
}
private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info) {
if (!canShare(info)) {
return null;
}
return new Share(launcher, info);
}
/**
* The Share App system shortcut, used to initiate p2p sharing of a given app
*/
public final class Share extends SystemShortcut<Launcher> {
public Share(Launcher target, ItemInfo itemInfo) {
super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
}
@Override
public void onClick(View view) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
ComponentName targetComponent = mItemInfo.getTargetComponent();
if (targetComponent == null) {
Log.e(TAG, "Item missing target component");
return;
}
String packageName = targetComponent.getPackageName();
PackageManager packageManager = view.getContext().getPackageManager();
String sourceDir, appLabel;
try {
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
sourceDir = packageInfo.applicationInfo.sourceDir;
appLabel = packageManager.getApplicationLabel(packageInfo.applicationInfo)
.toString() + APP_EXSTENSION;
} catch (Exception e) {
Log.e(TAG, "Could not find info for package \"" + packageName + "\"");
return;
}
Uri uri = getShareableUri(view.getContext(), sourceDir, appLabel);
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
sendIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
sendIntent.setType(APP_MIME_TYPE);
sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
mTarget.startActivitySafely(view, sendIntent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
/**
* Shortcut factory for generating the Share App button
*/
public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY = (launcher, itemInfo) ->
(new AppSharing(launcher)).getShortcut(launcher, itemInfo);
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 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 com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import java.util.stream.Stream;
/**
* The Launcher variant used for Android Go Edition
*/
public class Launcher3QuickStepGo extends QuickstepLauncher {
private static final String TAG = "Launcher3QuickStepGo";
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
Stream<SystemShortcut.Factory> shortcuts = super.getSupportedShortcuts();
if (AppSharing.ENABLE_APP_SHARING) {
shortcuts = Stream.concat(shortcuts, Stream.of(AppSharing.SHORTCUT_FACTORY));
}
return shortcuts;
}
}

View File

@ -2,4 +2,5 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="/" />
<files-path name="log_files" path="/" />
<root-path name="apps" path="/" />
</paths>