fix: 首次提交
This commit is contained in:
27
SettingsLib/Tile/Android.bp
Normal file
27
SettingsLib/Tile/Android.bp
Normal file
@@ -0,0 +1,27 @@
|
||||
package {
|
||||
// See: http://go/android-license-faq
|
||||
// A large-scale-change added 'default_applicable_licenses' to import
|
||||
// all of the 'license_kinds' from "frameworks_base_license"
|
||||
// to get the below license kinds:
|
||||
// SPDX-license-identifier-Apache-2.0
|
||||
default_applicable_licenses: ["frameworks_base_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SettingsLibTile",
|
||||
use_resource_processor: true,
|
||||
defaults: [
|
||||
"SettingsLintDefaults",
|
||||
],
|
||||
|
||||
srcs: ["src/**/*.java"],
|
||||
|
||||
static_libs: [
|
||||
"androidx.annotation_annotation",
|
||||
],
|
||||
|
||||
min_sdk_version: "21",
|
||||
lint: {
|
||||
baseline_filename: "lint-baseline.xml",
|
||||
},
|
||||
}
|
||||
23
SettingsLib/Tile/AndroidManifest.xml
Normal file
23
SettingsLib/Tile/AndroidManifest.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2019 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.settingslib.drawer">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
|
||||
</manifest>
|
||||
50
SettingsLib/Tile/build.gradle
Normal file
50
SettingsLib/Tile/build.gradle
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Include this gradle file if you are building against this as a standalone gradle library project,
|
||||
* as opposed to building it as part of the git-tree. This is typically the file you want to include
|
||||
* if you create a new project in Android Studio.
|
||||
*
|
||||
* For example, you can include the following in your settings.gradle file:
|
||||
* include ':setupcompat'
|
||||
* project(':setupcompat').projectDir = new File(PATH_TO_THIS_DIRECTORY)
|
||||
*
|
||||
* And then you can include the :setupcompat project as one of your dependencies
|
||||
* dependencies {
|
||||
* implementation project(path: ':setupcompat')
|
||||
* }
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
android {
|
||||
// Not specifying compileSdkVersion here so clients can specify it; must be at least Q
|
||||
namespace = "com.android.settingslib.drawer"
|
||||
compileSdk 34
|
||||
defaultConfig {
|
||||
minSdkVersion 31
|
||||
targetSdkVersion 34
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.flags'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.androidx.annotation.annotation
|
||||
}
|
||||
59
SettingsLib/Tile/lint-baseline.xml
Normal file
59
SettingsLib/Tile/lint-baseline.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 29 (current min is 21): `android.os.Parcel#writeBoolean`"
|
||||
errorLine1=" dest.writeBoolean(this instanceof ProviderTile);"
|
||||
errorLine2=" ~~~~~~~~~~~~">
|
||||
<location
|
||||
file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
|
||||
line="114"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#createWithResource`"
|
||||
errorLine1=" final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
|
||||
line="326"
|
||||
column="36"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 23 (current min is 21): `android.graphics.drawable.Icon#setTint`"
|
||||
errorLine1=" icon.setTint(tintColor);"
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
|
||||
line="332"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 29 (current min is 21): `android.os.Parcel#readBoolean`"
|
||||
errorLine1=" final boolean isProviderTile = source.readBoolean();"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java"
|
||||
line="387"
|
||||
column="51"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 31 (current min is 21): `android.content.Context#getAttributionSource`"
|
||||
errorLine1=" return provider.call(context.getAttributionSource(),"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java"
|
||||
line="601"
|
||||
column="42"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Description of a single dashboard tile which is generated from an activity.
|
||||
*/
|
||||
public class ActivityTile extends Tile {
|
||||
private static final String TAG = "ActivityTile";
|
||||
|
||||
public ActivityTile(ActivityInfo info, String category) {
|
||||
super(info, category, info.metaData);
|
||||
}
|
||||
|
||||
ActivityTile(Parcel in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Objects.hash(getPackageName(), getComponentName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return getPackageName() + "/" + getComponentName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentInfo getComponentInfo(Context context) {
|
||||
if (mComponentInfo == null) {
|
||||
final PackageManager pm = context.getApplicationContext().getPackageManager();
|
||||
final Intent intent = getIntent();
|
||||
final List<ResolveInfo> infoList =
|
||||
pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
|
||||
if (infoList != null && !infoList.isEmpty()) {
|
||||
mComponentInfo = infoList.get(0).activityInfo;
|
||||
setMetaData(mComponentInfo.metaData);
|
||||
} else {
|
||||
Log.e(TAG, "Cannot find package info for "
|
||||
+ intent.getComponent().flattenToString());
|
||||
}
|
||||
}
|
||||
return mComponentInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence getComponentLabel(Context context) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final ComponentInfo info = getComponentInfo(context);
|
||||
return info == null
|
||||
? null
|
||||
: info.loadLabel(pm);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getComponentIcon(ComponentInfo componentInfo) {
|
||||
return componentInfo.icon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import static java.lang.String.CASE_INSENSITIVE_ORDER;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The category for handle {@link Tile}
|
||||
*/
|
||||
public class DashboardCategory implements Parcelable {
|
||||
|
||||
/**
|
||||
* Key used for placing external tiles.
|
||||
*/
|
||||
public final String key;
|
||||
|
||||
/**
|
||||
* List of the category's children
|
||||
*/
|
||||
private List<Tile> mTiles = new ArrayList<>();
|
||||
|
||||
public DashboardCategory(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
DashboardCategory(Parcel in) {
|
||||
key = in.readString();
|
||||
|
||||
final int count = in.readInt();
|
||||
|
||||
for (int n = 0; n < count; n++) {
|
||||
Tile tile = Tile.CREATOR.createFromParcel(in);
|
||||
mTiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the list of the category's children.
|
||||
*
|
||||
* Note: the returned list serves as a read-only list. If tiles needs to be added or removed
|
||||
* from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
|
||||
*/
|
||||
public synchronized List<Tile> getTiles() {
|
||||
final List<Tile> result = new ArrayList<>(mTiles.size());
|
||||
for (Tile tile : mTiles) {
|
||||
result.add(tile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tile
|
||||
*/
|
||||
public synchronized void addTile(Tile tile) {
|
||||
mTiles.add(tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove tile
|
||||
*/
|
||||
public synchronized void removeTile(int n) {
|
||||
mTiles.remove(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of tile
|
||||
*/
|
||||
public int getTilesCount() {
|
||||
return mTiles.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tile
|
||||
*/
|
||||
public Tile getTile(int n) {
|
||||
return mTiles.get(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort priority value for tiles in this category.
|
||||
*/
|
||||
public void sortTiles() {
|
||||
Collections.sort(mTiles, Tile.TILE_COMPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort priority value and package name for tiles in this category.
|
||||
*/
|
||||
public synchronized void sortTiles(String skipPackageName) {
|
||||
// Sort mTiles based on [order, package within order]
|
||||
Collections.sort(mTiles, (tile1, tile2) -> {
|
||||
// First sort by order
|
||||
final int orderCompare = tile2.getOrder() - tile1.getOrder();
|
||||
if (orderCompare != 0) {
|
||||
return orderCompare;
|
||||
}
|
||||
|
||||
// Then sort by package name, skip package take precedence
|
||||
final String package1 = tile1.getPackageName();
|
||||
final String package2 = tile2.getPackageName();
|
||||
final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
|
||||
if (packageCompare != 0) {
|
||||
if (TextUtils.equals(package1, skipPackageName)) {
|
||||
return -1;
|
||||
}
|
||||
if (TextUtils.equals(package2, skipPackageName)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return packageCompare;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(key);
|
||||
|
||||
final int count = mTiles.size();
|
||||
dest.writeInt(count);
|
||||
|
||||
for (int n = 0; n < count; n++) {
|
||||
Tile tile = mTiles.get(n);
|
||||
tile.writeToParcel(dest, flags);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<DashboardCategory> CREATOR = new Creator<DashboardCategory>() {
|
||||
public DashboardCategory createFromParcel(Parcel source) {
|
||||
return new DashboardCategory(source);
|
||||
}
|
||||
|
||||
public DashboardCategory[] newArray(int size) {
|
||||
return new DashboardCategory[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
/** Interface for {@link EntryController} whose instances support dynamic summary */
|
||||
public interface DynamicSummary {
|
||||
/** @return the dynamic summary text */
|
||||
String getDynamicSummary();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
/** Interface for {@link EntryController} whose instances support dynamic title */
|
||||
public interface DynamicTitle {
|
||||
/** @return the dynamic title text */
|
||||
String getDynamicTitle();
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settingslib.drawer;
|
||||
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An abstract class for injecting entries to Settings.
|
||||
*/
|
||||
public abstract class EntriesProvider extends ContentProvider {
|
||||
private static final String TAG = "EntriesProvider";
|
||||
|
||||
public static final String METHOD_GET_ENTRY_DATA = "getEntryData";
|
||||
public static final String METHOD_GET_PROVIDER_ICON = "getProviderIcon";
|
||||
public static final String METHOD_GET_DYNAMIC_TITLE = "getDynamicTitle";
|
||||
public static final String METHOD_GET_DYNAMIC_SUMMARY = "getDynamicSummary";
|
||||
public static final String METHOD_IS_CHECKED = "isChecked";
|
||||
public static final String METHOD_ON_CHECKED_CHANGED = "onCheckedChanged";
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #METHOD_GET_ENTRY_DATA} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String METHOD_GET_SWITCH_DATA = "getSwitchData";
|
||||
|
||||
public static final String EXTRA_ENTRY_DATA = "entry_data";
|
||||
public static final String EXTRA_SWITCH_CHECKED_STATE = "checked_state";
|
||||
public static final String EXTRA_SWITCH_SET_CHECKED_ERROR = "set_checked_error";
|
||||
public static final String EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE = "set_checked_error_message";
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #EXTRA_ENTRY_DATA} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String EXTRA_SWITCH_DATA = "switch_data";
|
||||
|
||||
private String mAuthority;
|
||||
private final Map<String, EntryController> mControllerMap = new LinkedHashMap<>();
|
||||
private final List<Bundle> mEntryDataList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Get a list of {@link EntryController} for this provider.
|
||||
*/
|
||||
protected abstract List<? extends EntryController> createEntryControllers();
|
||||
|
||||
protected EntryController getController(String key) {
|
||||
return mControllerMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachInfo(Context context, ProviderInfo info) {
|
||||
mAuthority = info.authority;
|
||||
Log.i(TAG, mAuthority);
|
||||
super.attachInfo(context, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
final List<? extends EntryController> controllers = createEntryControllers();
|
||||
if (controllers == null || controllers.isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
for (EntryController controller : controllers) {
|
||||
final String key = controller.getKey();
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
throw new NullPointerException("Entry key cannot be null: "
|
||||
+ controller.getClass().getSimpleName());
|
||||
} else if (mControllerMap.containsKey(key)) {
|
||||
throw new IllegalArgumentException("Entry key " + key + " is duplicated by: "
|
||||
+ controller.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
controller.setAuthority(mAuthority);
|
||||
mControllerMap.put(key, controller);
|
||||
if (!(controller instanceof PrimarySwitchController)) {
|
||||
mEntryDataList.add(controller.getBundle());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle call(String method, String uriString, Bundle extras) {
|
||||
final Bundle bundle = new Bundle();
|
||||
final String key = extras != null
|
||||
? extras.getString(META_DATA_PREFERENCE_KEYHINT)
|
||||
: null;
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
switch (method) {
|
||||
case METHOD_GET_ENTRY_DATA:
|
||||
bundle.putParcelableList(EXTRA_ENTRY_DATA, mEntryDataList);
|
||||
return bundle;
|
||||
case METHOD_GET_SWITCH_DATA:
|
||||
bundle.putParcelableList(EXTRA_SWITCH_DATA, mEntryDataList);
|
||||
return bundle;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final EntryController controller = mControllerMap.get(key);
|
||||
if (controller == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case METHOD_GET_ENTRY_DATA:
|
||||
case METHOD_GET_SWITCH_DATA:
|
||||
if (!(controller instanceof PrimarySwitchController)) {
|
||||
return controller.getBundle();
|
||||
}
|
||||
break;
|
||||
case METHOD_GET_PROVIDER_ICON:
|
||||
if (controller instanceof ProviderIcon) {
|
||||
return ((ProviderIcon) controller).getProviderIcon();
|
||||
}
|
||||
break;
|
||||
case METHOD_GET_DYNAMIC_TITLE:
|
||||
if (controller instanceof DynamicTitle) {
|
||||
bundle.putString(META_DATA_PREFERENCE_TITLE,
|
||||
((DynamicTitle) controller).getDynamicTitle());
|
||||
return bundle;
|
||||
}
|
||||
break;
|
||||
case METHOD_GET_DYNAMIC_SUMMARY:
|
||||
if (controller instanceof DynamicSummary) {
|
||||
bundle.putString(META_DATA_PREFERENCE_SUMMARY,
|
||||
((DynamicSummary) controller).getDynamicSummary());
|
||||
return bundle;
|
||||
}
|
||||
break;
|
||||
case METHOD_IS_CHECKED:
|
||||
if (controller instanceof ProviderSwitch) {
|
||||
bundle.putBoolean(EXTRA_SWITCH_CHECKED_STATE,
|
||||
((ProviderSwitch) controller).isSwitchChecked());
|
||||
return bundle;
|
||||
}
|
||||
break;
|
||||
case METHOD_ON_CHECKED_CHANGED:
|
||||
if (controller instanceof ProviderSwitch) {
|
||||
return onSwitchCheckedChanged(extras.getBoolean(EXTRA_SWITCH_CHECKED_STATE),
|
||||
(ProviderSwitch) controller);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Bundle onSwitchCheckedChanged(boolean checked, ProviderSwitch controller) {
|
||||
final boolean success = controller.onSwitchCheckedChanged(checked);
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, !success);
|
||||
if (success) {
|
||||
if (controller instanceof DynamicSummary) {
|
||||
((EntryController) controller).notifySummaryChanged(getContext());
|
||||
}
|
||||
} else {
|
||||
bundle.putString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE,
|
||||
controller.getSwitchErrorMessage(checked));
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settingslib.drawer;
|
||||
|
||||
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
|
||||
import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.EXTRA_CATEGORY_KEY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_PENDING_INTENT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
/**
|
||||
* A controller that manages events for switch.
|
||||
*/
|
||||
public abstract class EntryController {
|
||||
|
||||
private String mAuthority;
|
||||
|
||||
/**
|
||||
* Returns the key for this switch.
|
||||
*/
|
||||
public abstract String getKey();
|
||||
|
||||
/**
|
||||
* Returns the {@link MetaData} for this switch.
|
||||
*/
|
||||
protected abstract MetaData getMetaData();
|
||||
|
||||
/**
|
||||
* Notify registered observers that title was updated and attempt to sync changes.
|
||||
*/
|
||||
public void notifyTitleChanged(Context context) {
|
||||
if (this instanceof DynamicTitle) {
|
||||
notifyChanged(context, METHOD_GET_DYNAMIC_TITLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify registered observers that summary was updated and attempt to sync changes.
|
||||
*/
|
||||
public void notifySummaryChanged(Context context) {
|
||||
if (this instanceof DynamicSummary) {
|
||||
notifyChanged(context, METHOD_GET_DYNAMIC_SUMMARY);
|
||||
}
|
||||
}
|
||||
|
||||
void setAuthority(String authority) {
|
||||
mAuthority = authority;
|
||||
}
|
||||
|
||||
Bundle getBundle() {
|
||||
final MetaData metaData = getMetaData();
|
||||
if (metaData == null) {
|
||||
throw new NullPointerException("Should not return null in getMetaData()");
|
||||
}
|
||||
|
||||
final Bundle bundle = metaData.build();
|
||||
final String uriString = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(mAuthority)
|
||||
.build()
|
||||
.toString();
|
||||
bundle.putString(META_DATA_PREFERENCE_KEYHINT, getKey());
|
||||
if (this instanceof ProviderIcon) {
|
||||
bundle.putString(META_DATA_PREFERENCE_ICON_URI, uriString);
|
||||
}
|
||||
if (this instanceof DynamicTitle) {
|
||||
bundle.putString(META_DATA_PREFERENCE_TITLE_URI, uriString);
|
||||
}
|
||||
if (this instanceof DynamicSummary) {
|
||||
bundle.putString(META_DATA_PREFERENCE_SUMMARY_URI, uriString);
|
||||
}
|
||||
if (this instanceof ProviderSwitch) {
|
||||
bundle.putString(META_DATA_PREFERENCE_SWITCH_URI, uriString);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
private void notifyChanged(Context context, String method) {
|
||||
final Uri uri = TileUtils.buildUri(mAuthority, method, getKey());
|
||||
context.getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all meta data of the item.
|
||||
*/
|
||||
protected static class MetaData {
|
||||
private String mCategory;
|
||||
private int mOrder;
|
||||
@DrawableRes
|
||||
private int mIcon;
|
||||
private int mIconBackgroundHint;
|
||||
private int mIconBackgroundArgb;
|
||||
private Boolean mIconTintable;
|
||||
@StringRes
|
||||
private int mTitleId;
|
||||
private String mTitle;
|
||||
@StringRes
|
||||
private int mSummaryId;
|
||||
private String mSummary;
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
/**
|
||||
* @param category the category of the switch. This value must be from {@link CategoryKey}.
|
||||
*/
|
||||
public MetaData(@NonNull String category) {
|
||||
mCategory = category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of the item that should be displayed on screen. Bigger value items displays
|
||||
* closer on top.
|
||||
*/
|
||||
public MetaData setOrder(int order) {
|
||||
mOrder = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the icon that should be displayed for the item. */
|
||||
public MetaData setIcon(@DrawableRes int icon) {
|
||||
mIcon = icon;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the icon background color. The value may or may not be used by Settings app. */
|
||||
public MetaData setIconBackgoundHint(int hint) {
|
||||
mIconBackgroundHint = hint;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the icon background color as raw ARGB. */
|
||||
public MetaData setIconBackgoundArgb(int argb) {
|
||||
mIconBackgroundArgb = argb;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Specify whether the icon is tintable. */
|
||||
public MetaData setIconTintable(boolean tintable) {
|
||||
mIconTintable = tintable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the title that should be displayed for the item. */
|
||||
public MetaData setTitle(@StringRes int id) {
|
||||
mTitleId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the title that should be displayed for the item. */
|
||||
public MetaData setTitle(String title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the summary text that should be displayed for the item. */
|
||||
public MetaData setSummary(@StringRes int id) {
|
||||
mSummaryId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the summary text that should be displayed for the item. */
|
||||
public MetaData setSummary(String summary) {
|
||||
mSummary = summary;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MetaData setPendingIntent(PendingIntent pendingIntent) {
|
||||
mPendingIntent = pendingIntent;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Bundle build() {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString(EXTRA_CATEGORY_KEY, mCategory);
|
||||
|
||||
if (mOrder != 0) {
|
||||
bundle.putInt(META_DATA_KEY_ORDER, mOrder);
|
||||
}
|
||||
|
||||
if (mIcon != 0) {
|
||||
bundle.putInt(META_DATA_PREFERENCE_ICON, mIcon);
|
||||
}
|
||||
if (mIconBackgroundHint != 0) {
|
||||
bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, mIconBackgroundHint);
|
||||
}
|
||||
if (mIconBackgroundArgb != 0) {
|
||||
bundle.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, mIconBackgroundArgb);
|
||||
}
|
||||
if (mIconTintable != null) {
|
||||
bundle.putBoolean(META_DATA_PREFERENCE_ICON_TINTABLE, mIconTintable);
|
||||
}
|
||||
|
||||
if (mTitleId != 0) {
|
||||
bundle.putInt(META_DATA_PREFERENCE_TITLE, mTitleId);
|
||||
} else if (mTitle != null) {
|
||||
bundle.putString(META_DATA_PREFERENCE_TITLE, mTitle);
|
||||
}
|
||||
|
||||
if (mSummaryId != 0) {
|
||||
bundle.putInt(META_DATA_PREFERENCE_SUMMARY, mSummaryId);
|
||||
} else if (mSummary != null) {
|
||||
bundle.putString(META_DATA_PREFERENCE_SUMMARY, mSummary);
|
||||
}
|
||||
|
||||
if (mPendingIntent != null) {
|
||||
bundle.putParcelable(META_DATA_PREFERENCE_PENDING_INTENT, mPendingIntent);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* A controller that manages event for Primary switch.
|
||||
*/
|
||||
public abstract class PrimarySwitchController extends SwitchController {
|
||||
|
||||
@Override
|
||||
protected final MetaData getMetaData() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
final Bundle getBundle() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Interface for {@link EntryController} whose instances support icon provided from the content
|
||||
* provider
|
||||
*/
|
||||
public interface ProviderIcon {
|
||||
/**
|
||||
* @return the bundle of icon info including {@link TileUtils#EXTRA_PREFERENCE_ICON_PACKAGE} and
|
||||
* {@link TileUtils#META_DATA_PREFERENCE_ICON}.
|
||||
*/
|
||||
Bundle getProviderIcon();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.settingslib.drawer;
|
||||
|
||||
/**
|
||||
* Interface for {@link EntryController} whose instances support switch widget provided from the
|
||||
* content provider
|
||||
*/
|
||||
public interface ProviderSwitch {
|
||||
/**
|
||||
* Returns the checked state of this switch.
|
||||
*/
|
||||
boolean isSwitchChecked();
|
||||
|
||||
/**
|
||||
* Called when the checked state of this switch is changed.
|
||||
*
|
||||
* @return true if the checked state was successfully changed, otherwise false
|
||||
*/
|
||||
boolean onSwitchCheckedChanged(boolean checked);
|
||||
|
||||
/**
|
||||
* Returns the error message which will be toasted when {@link #onSwitchCheckedChanged} returns
|
||||
* false.
|
||||
*/
|
||||
String getSwitchErrorMessage(boolean attemptedChecked);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Description of a single dashboard tile which is generated from a content provider.
|
||||
*/
|
||||
public class ProviderTile extends Tile {
|
||||
private static final String TAG = "ProviderTile";
|
||||
|
||||
private static final boolean DEBUG_TIMING = false;
|
||||
|
||||
private String mAuthority;
|
||||
private String mKey;
|
||||
|
||||
public ProviderTile(ProviderInfo info, String category, Bundle metaData) {
|
||||
super(info, category, metaData);
|
||||
mAuthority = info.authority;
|
||||
mKey = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
|
||||
}
|
||||
|
||||
ProviderTile(Parcel in) {
|
||||
super(in);
|
||||
mAuthority = ((ProviderInfo) mComponentInfo).authority;
|
||||
mKey = getMetaData().getString(META_DATA_PREFERENCE_KEYHINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Objects.hash(mAuthority, mKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return mAuthority + "/" + mKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ComponentInfo getComponentInfo(Context context) {
|
||||
if (mComponentInfo == null) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final PackageManager pm = context.getApplicationContext().getPackageManager();
|
||||
final Intent intent = getIntent();
|
||||
final List<ResolveInfo> infoList =
|
||||
pm.queryIntentContentProviders(intent, 0 /* flags */);
|
||||
if (infoList != null && !infoList.isEmpty()) {
|
||||
final ProviderInfo providerInfo = infoList.get(0).providerInfo;
|
||||
mComponentInfo = providerInfo;
|
||||
setMetaData(TileUtils.getEntryDataFromProvider(context, providerInfo.authority,
|
||||
mKey));
|
||||
} else {
|
||||
Log.e(TAG, "Cannot find package info for "
|
||||
+ intent.getComponent().flattenToString());
|
||||
}
|
||||
|
||||
if (DEBUG_TIMING) {
|
||||
Log.d(TAG, "getComponentInfo took "
|
||||
+ (System.currentTimeMillis() - startTime) + " ms");
|
||||
}
|
||||
}
|
||||
return mComponentInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence getComponentLabel(Context context) {
|
||||
// Getting provider label for a tile title isn't supported.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getComponentIcon(ComponentInfo info) {
|
||||
// Getting provider icon for a tile title isn't supported.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A controller that manages events for switch.
|
||||
*
|
||||
* @deprecated Use {@link EntriesProvider} with {@link ProviderSwitch} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class SwitchController extends EntryController implements ProviderSwitch {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key for this switch.
|
||||
*/
|
||||
public abstract String getSwitchKey();
|
||||
|
||||
/**
|
||||
* Returns the checked state of this switch.
|
||||
*/
|
||||
protected abstract boolean isChecked();
|
||||
|
||||
/**
|
||||
* Called when the checked state of this switch is changed.
|
||||
*
|
||||
* @return true if the checked state was successfully changed, otherwise false
|
||||
*/
|
||||
protected abstract boolean onCheckedChanged(boolean checked);
|
||||
|
||||
/**
|
||||
* Returns the error message which will be toasted when {@link #onCheckedChanged} returns false.
|
||||
*/
|
||||
protected abstract String getErrorMessage(boolean attemptedChecked);
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return getSwitchKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSwitchChecked() {
|
||||
return isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSwitchCheckedChanged(boolean checked) {
|
||||
return onCheckedChanged(checked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSwitchErrorMessage(boolean attemptedChecked) {
|
||||
return getErrorMessage(attemptedChecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link EntryController.MetaData}, for backwards compatibility purpose.
|
||||
*
|
||||
* @deprecated Use {@link EntryController.MetaData} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
protected static class MetaData extends EntryController.MetaData {
|
||||
/**
|
||||
* @param category the category of the switch. This value must be from {@link CategoryKey}.
|
||||
*
|
||||
* @deprecated Use {@link EntryController.MetaData} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public MetaData(@NonNull String category) {
|
||||
super(category);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An abstract class for injecting switches to Settings.
|
||||
*
|
||||
* @deprecated Use {@link EntriesProvider} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class SwitchesProvider extends EntriesProvider {
|
||||
|
||||
/**
|
||||
* Get a list of {@link SwitchController} for this provider.
|
||||
*/
|
||||
protected abstract List<SwitchController> createSwitchControllers();
|
||||
|
||||
@Override
|
||||
protected List<? extends EntryController> createEntryControllers() {
|
||||
return createSwitchControllers();
|
||||
}
|
||||
}
|
||||
491
SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
Normal file
491
SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
|
||||
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
|
||||
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
|
||||
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Description of a single dashboard tile that the user can select.
|
||||
*/
|
||||
public abstract class Tile implements Parcelable {
|
||||
|
||||
private static final String TAG = "Tile";
|
||||
|
||||
/**
|
||||
* Optional list of user handles which the intent should be launched on.
|
||||
*/
|
||||
public ArrayList<UserHandle> userHandle = new ArrayList<>();
|
||||
|
||||
public HashMap<UserHandle, PendingIntent> pendingIntentMap = new HashMap<>();
|
||||
|
||||
@VisibleForTesting
|
||||
long mLastUpdateTime;
|
||||
private final String mComponentPackage;
|
||||
private final String mComponentName;
|
||||
private final Intent mIntent;
|
||||
|
||||
protected ComponentInfo mComponentInfo;
|
||||
private CharSequence mSummaryOverride;
|
||||
private Bundle mMetaData;
|
||||
private String mCategory;
|
||||
|
||||
public Tile(ComponentInfo info, String category, Bundle metaData) {
|
||||
mComponentInfo = info;
|
||||
mComponentPackage = mComponentInfo.packageName;
|
||||
mComponentName = mComponentInfo.name;
|
||||
mCategory = category;
|
||||
mMetaData = metaData;
|
||||
mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
|
||||
if (isNewTask()) {
|
||||
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
}
|
||||
|
||||
Tile(Parcel in) {
|
||||
mComponentPackage = in.readString();
|
||||
mComponentName = in.readString();
|
||||
mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
|
||||
final int number = in.readInt();
|
||||
for (int i = 0; i < number; i++) {
|
||||
userHandle.add(UserHandle.CREATOR.createFromParcel(in));
|
||||
}
|
||||
mCategory = in.readString();
|
||||
mMetaData = in.readBundle();
|
||||
if (isNewTask()) {
|
||||
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeBoolean(this instanceof ProviderTile);
|
||||
dest.writeString(mComponentPackage);
|
||||
dest.writeString(mComponentName);
|
||||
final int size = userHandle.size();
|
||||
dest.writeInt(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
userHandle.get(i).writeToParcel(dest, flags);
|
||||
}
|
||||
dest.writeString(mCategory);
|
||||
dest.writeBundle(mMetaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique ID of the tile
|
||||
*/
|
||||
public abstract int getId();
|
||||
|
||||
/**
|
||||
* Human-readable description of the tile
|
||||
*/
|
||||
public abstract String getDescription();
|
||||
|
||||
protected abstract ComponentInfo getComponentInfo(Context context);
|
||||
|
||||
protected abstract CharSequence getComponentLabel(Context context);
|
||||
|
||||
protected abstract int getComponentIcon(ComponentInfo info);
|
||||
|
||||
public String getPackageName() {
|
||||
return mComponentPackage;
|
||||
}
|
||||
|
||||
public String getComponentName() {
|
||||
return mComponentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intent to launch when the preference is selected.
|
||||
*/
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Category in which the tile should be placed.
|
||||
*/
|
||||
public String getCategory() {
|
||||
return mCategory;
|
||||
}
|
||||
|
||||
public void setCategory(String newCategoryKey) {
|
||||
mCategory = newCategoryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Priority of this tile, used for display ordering.
|
||||
*/
|
||||
public int getOrder() {
|
||||
if (hasOrder()) {
|
||||
return mMetaData.getInt(META_DATA_KEY_ORDER);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether tile has order.
|
||||
*/
|
||||
public boolean hasOrder() {
|
||||
return mMetaData != null
|
||||
&& mMetaData.containsKey(META_DATA_KEY_ORDER)
|
||||
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether tile has a switch.
|
||||
*/
|
||||
public boolean hasSwitch() {
|
||||
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SWITCH_URI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether tile has a pending intent.
|
||||
*/
|
||||
public boolean hasPendingIntent() {
|
||||
return !pendingIntentMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Title of the tile that is shown to the user.
|
||||
*/
|
||||
public CharSequence getTitle(Context context) {
|
||||
CharSequence title = null;
|
||||
ensureMetadataNotStale(context);
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
|
||||
if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
|
||||
// If has as uri to provide dynamic title, skip loading here. UI will later load
|
||||
// at tile binding time.
|
||||
return null;
|
||||
}
|
||||
if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
|
||||
try {
|
||||
final Resources res =
|
||||
packageManager.getResourcesForApplication(mComponentPackage);
|
||||
title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
|
||||
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
|
||||
Log.w(TAG, "Couldn't find info", e);
|
||||
}
|
||||
} else {
|
||||
title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
|
||||
}
|
||||
}
|
||||
// Set the preference title by the component if no meta-data is found
|
||||
if (title == null) {
|
||||
title = getComponentLabel(context);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
|
||||
*/
|
||||
public void overrideSummary(CharSequence summaryOverride) {
|
||||
mSummaryOverride = summaryOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional summary describing what this tile controls.
|
||||
*/
|
||||
public CharSequence getSummary(Context context) {
|
||||
if (mSummaryOverride != null) {
|
||||
return mSummaryOverride;
|
||||
}
|
||||
ensureMetadataNotStale(context);
|
||||
CharSequence summary = null;
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
if (mMetaData != null) {
|
||||
if (mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
|
||||
// If has as uri to provide dynamic summary, skip loading here. UI will later load
|
||||
// at tile binding time.
|
||||
return null;
|
||||
}
|
||||
if (mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
|
||||
if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
|
||||
try {
|
||||
final Resources res =
|
||||
packageManager.getResourcesForApplication(mComponentPackage);
|
||||
summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY));
|
||||
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
|
||||
Log.d(TAG, "Couldn't find info", e);
|
||||
}
|
||||
} else {
|
||||
summary = mMetaData.getString(META_DATA_PREFERENCE_SUMMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setMetaData(Bundle metaData) {
|
||||
mMetaData = metaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The metaData from the activity that defines this tile.
|
||||
*/
|
||||
public Bundle getMetaData() {
|
||||
return mMetaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional key to use for this tile.
|
||||
*/
|
||||
public String getKey(Context context) {
|
||||
ensureMetadataNotStale(context);
|
||||
if (!hasKey()) {
|
||||
return null;
|
||||
}
|
||||
if (mMetaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
|
||||
return context.getResources().getString(mMetaData.getInt(META_DATA_PREFERENCE_KEYHINT));
|
||||
} else {
|
||||
return mMetaData.getString(META_DATA_PREFERENCE_KEYHINT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether title has key.
|
||||
*/
|
||||
public boolean hasKey() {
|
||||
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional icon to show for this tile.
|
||||
*
|
||||
* @attr ref android.R.styleable#PreferenceHeader_icon
|
||||
*/
|
||||
public Icon getIcon(Context context) {
|
||||
if (context == null || mMetaData == null) {
|
||||
return null;
|
||||
}
|
||||
ensureMetadataNotStale(context);
|
||||
final ComponentInfo componentInfo = getComponentInfo(context);
|
||||
if (componentInfo == null) {
|
||||
Log.w(TAG, "Cannot find ComponentInfo for " + getDescription());
|
||||
return null;
|
||||
}
|
||||
|
||||
int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
|
||||
// Set the icon. Skip the transparent color for backward compatibility since Android S.
|
||||
if (iconResId != 0 && iconResId != android.R.color.transparent) {
|
||||
final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);
|
||||
if (isIconTintable(context)) {
|
||||
final TypedArray a = context.obtainStyledAttributes(new int[]{
|
||||
android.R.attr.colorControlNormal});
|
||||
final int tintColor = a.getColor(0, 0);
|
||||
a.recycle();
|
||||
icon.setTint(tintColor);
|
||||
}
|
||||
return icon;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the icon can be tinted. This is true when icon needs to be monochrome (single-color)
|
||||
*/
|
||||
public boolean isIconTintable(Context context) {
|
||||
ensureMetadataNotStale(context);
|
||||
if (mMetaData != null
|
||||
&& mMetaData.containsKey(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE)) {
|
||||
return mMetaData.getBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the {@link Activity} should be launched in a separate task.
|
||||
*/
|
||||
public boolean isNewTask() {
|
||||
if (mMetaData != null
|
||||
&& mMetaData.containsKey(META_DATA_NEW_TASK)) {
|
||||
return mMetaData.getBoolean(META_DATA_NEW_TASK);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures metadata is not stale for this tile.
|
||||
*/
|
||||
private void ensureMetadataNotStale(Context context) {
|
||||
final PackageManager pm = context.getApplicationContext().getPackageManager();
|
||||
|
||||
try {
|
||||
final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,
|
||||
PackageManager.GET_META_DATA).lastUpdateTime;
|
||||
if (lastUpdateTime == mLastUpdateTime) {
|
||||
// All good. Do nothing
|
||||
return;
|
||||
}
|
||||
// App has been updated since we load metadata last time. Reload metadata.
|
||||
mComponentInfo = null;
|
||||
getComponentInfo(context);
|
||||
mLastUpdateTime = lastUpdateTime;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.d(TAG, "Can't find package, probably uninstalled.");
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<Tile> CREATOR = new Creator<Tile>() {
|
||||
public Tile createFromParcel(Parcel source) {
|
||||
final boolean isProviderTile = source.readBoolean();
|
||||
// reset the Parcel pointer before delegating to the real constructor.
|
||||
source.setDataPosition(0);
|
||||
return isProviderTile ? new ProviderTile(source) : new ActivityTile(source);
|
||||
}
|
||||
|
||||
public Tile[] newArray(int size) {
|
||||
return new Tile[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether tile only has primary profile.
|
||||
*/
|
||||
public boolean isPrimaryProfileOnly() {
|
||||
return isPrimaryProfileOnly(mMetaData);
|
||||
}
|
||||
|
||||
static boolean isPrimaryProfileOnly(Bundle metaData) {
|
||||
String profile = metaData != null
|
||||
? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
|
||||
profile = (profile != null ? profile : PROFILE_ALL);
|
||||
return TextUtils.equals(profile, PROFILE_PRIMARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the tile belongs to another group / category.
|
||||
*/
|
||||
public boolean hasGroupKey() {
|
||||
return mMetaData != null
|
||||
&& !TextUtils.isEmpty(mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group / category key this tile belongs to.
|
||||
*/
|
||||
public String getGroupKey() {
|
||||
return (mMetaData == null) ? null : mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this is searchable.
|
||||
*/
|
||||
public boolean isSearchable() {
|
||||
return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the tile.
|
||||
*/
|
||||
public enum Type {
|
||||
/**
|
||||
* A preference that can be tapped on to open a new page.
|
||||
*/
|
||||
ACTION,
|
||||
|
||||
/**
|
||||
* A preference that can be tapped on to open an external app.
|
||||
*/
|
||||
EXTERNAL_ACTION,
|
||||
|
||||
/**
|
||||
* A preference that shows an on / off switch that can be toggled by the user.
|
||||
*/
|
||||
SWITCH,
|
||||
|
||||
/**
|
||||
* A preference with both an on / off switch, and a tappable area that can perform an
|
||||
* action.
|
||||
*/
|
||||
SWITCH_WITH_ACTION,
|
||||
|
||||
/**
|
||||
* A preference category with a title that can be used to group multiple preferences
|
||||
* together.
|
||||
*/
|
||||
GROUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the tile.
|
||||
*
|
||||
* @see Type
|
||||
*/
|
||||
public Type getType() {
|
||||
boolean hasExternalAction = hasPendingIntent();
|
||||
boolean hasAction = hasExternalAction || this instanceof ActivityTile;
|
||||
boolean hasSwitch = hasSwitch();
|
||||
|
||||
if (hasSwitch && hasAction) {
|
||||
return Type.SWITCH_WITH_ACTION;
|
||||
} else if (hasSwitch) {
|
||||
return Type.SWITCH;
|
||||
} else if (hasExternalAction) {
|
||||
return Type.EXTERNAL_ACTION;
|
||||
} else if (hasAction) {
|
||||
return Type.ACTION;
|
||||
} else {
|
||||
return Type.GROUP;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Comparator<Tile> TILE_COMPARATOR =
|
||||
(lhs, rhs) -> rhs.getOrder() - lhs.getOrder();
|
||||
}
|
||||
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settingslib.drawer;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings.Global;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utils is a helper class that contains profile key, meta data, settings action
|
||||
* and static methods for get icon or text from uri.
|
||||
*/
|
||||
public class TileUtils {
|
||||
|
||||
private static final boolean DEBUG_TIMING = false;
|
||||
|
||||
private static final String LOG_TAG = "TileUtils";
|
||||
@VisibleForTesting
|
||||
static final String SETTING_PKG = "com.android.settings";
|
||||
|
||||
/**
|
||||
* Settings will search for system activities of this action and add them as a top level
|
||||
* settings tile using the following parameters.
|
||||
*
|
||||
* <p>A category must be specified in the meta-data for the activity named
|
||||
* {@link #EXTRA_CATEGORY_KEY}
|
||||
*
|
||||
* <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE}
|
||||
* otherwise the label for the activity will be used.
|
||||
*
|
||||
* <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON}
|
||||
* otherwise the icon for the activity will be used.
|
||||
*
|
||||
* <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
|
||||
*/
|
||||
public static final String EXTRA_SETTINGS_ACTION = "com.android.settings.action.EXTRA_SETTINGS";
|
||||
|
||||
/**
|
||||
* @See {@link #EXTRA_SETTINGS_ACTION}.
|
||||
*/
|
||||
public static final String IA_SETTINGS_ACTION = "com.android.settings.action.IA_SETTINGS";
|
||||
|
||||
/**
|
||||
* Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
|
||||
*/
|
||||
private static final String SETTINGS_ACTION = "com.android.settings.action.SETTINGS";
|
||||
|
||||
private static final String OPERATOR_SETTINGS =
|
||||
"com.android.settings.OPERATOR_APPLICATION_SETTING";
|
||||
|
||||
private static final String OPERATOR_DEFAULT_CATEGORY =
|
||||
"com.android.settings.category.wireless";
|
||||
|
||||
private static final String MANUFACTURER_SETTINGS =
|
||||
"com.android.settings.MANUFACTURER_APPLICATION_SETTING";
|
||||
|
||||
private static final String MANUFACTURER_DEFAULT_CATEGORY =
|
||||
"com.android.settings.category.device";
|
||||
|
||||
/**
|
||||
* The key used to get the category from metadata of activities of action
|
||||
* {@link #EXTRA_SETTINGS_ACTION}
|
||||
* The value must be from {@link CategoryKey}.
|
||||
*/
|
||||
static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
|
||||
|
||||
/**
|
||||
* The key used to get the package name of the icon resource for the preference.
|
||||
*/
|
||||
static final String EXTRA_PREFERENCE_ICON_PACKAGE = "com.android.settings.icon_package";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the key that should be used for the preference.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_KEYHINT = "com.android.settings.keyhint";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that can be set in the AndroidManifest.xml or in the content
|
||||
* provider to specify the key of a group / category where this preference belongs to.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_GROUP_KEY = "com.android.settings.group_key";
|
||||
|
||||
/**
|
||||
* Order of the item that should be displayed on screen. Bigger value items displays closer on
|
||||
* top.
|
||||
*/
|
||||
public static final String META_DATA_KEY_ORDER = "com.android.settings.order";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the icon that should be displayed for the preference.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the icon background color. The value may or may not be used by Settings app.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT =
|
||||
"com.android.settings.bg.hint";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the icon background color as raw ARGB.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB =
|
||||
"com.android.settings.bg.argb";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the content provider providing the icon that should be displayed for
|
||||
* the preference.
|
||||
*
|
||||
* Icon provided by the content provider overrides any static icon.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_ICON_URI = "com.android.settings.icon_uri";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify whether the icon is tintable. This should be a boolean value {@code true} or
|
||||
* {@code false}, set using {@code android:value}
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_ICON_TINTABLE =
|
||||
"com.android.settings.icon_tintable";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the title that should be displayed for the preference.
|
||||
*
|
||||
* <p>Note: It is preferred to provide this value using {@code android:resource} with a string
|
||||
* resource for localization.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the content provider providing the title text that should be displayed for the
|
||||
* preference.
|
||||
*
|
||||
* Title provided by the content provider overrides any static title.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_TITLE_URI =
|
||||
"com.android.settings.title_uri";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the summary text that should be displayed for the preference.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the content provider providing the summary text that should be displayed for the
|
||||
* preference.
|
||||
*
|
||||
* Summary provided by the content provider overrides any static summary.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
|
||||
"com.android.settings.summary_uri";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the content provider providing the switch that should be displayed for the
|
||||
* preference.
|
||||
*
|
||||
* This works with {@link #META_DATA_PREFERENCE_KEYHINT} which should also be set in the
|
||||
* AndroidManifest.xml
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_SWITCH_URI =
|
||||
"com.android.settings.switch_uri";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that can be set from the content provider providing the intent
|
||||
* that will be executed when the user taps on the preference.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_PENDING_INTENT =
|
||||
"com.android.settings.pending_intent";
|
||||
|
||||
/**
|
||||
* Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
|
||||
* the app will always be run in the primary profile.
|
||||
*
|
||||
* @see #META_DATA_KEY_PROFILE
|
||||
*/
|
||||
public static final String PROFILE_PRIMARY = "primary_profile_only";
|
||||
|
||||
/**
|
||||
* Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
|
||||
* will be presented with a dialog to choose the profile the app will be run in.
|
||||
*
|
||||
* @see #META_DATA_KEY_PROFILE
|
||||
*/
|
||||
public static final String PROFILE_ALL = "all_profiles";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify the profile in which the app should be run when the device has a managed profile.
|
||||
* The default value is {@link #PROFILE_ALL} which means the user will be presented with a
|
||||
* dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
|
||||
* run in the primary profile.
|
||||
*
|
||||
* @see #PROFILE_PRIMARY
|
||||
* @see #PROFILE_ALL
|
||||
*/
|
||||
public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml
|
||||
* to specify whether the {@link android.app.Activity} should be launched in a separate task.
|
||||
* This should be a boolean value {@code true} or {@code false}, set using {@code android:value}
|
||||
*/
|
||||
public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
|
||||
|
||||
/**
|
||||
* If the entry should be shown in settings search results. Defaults to true.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable";
|
||||
|
||||
/**
|
||||
* Build a list of DashboardCategory.
|
||||
*/
|
||||
public static List<DashboardCategory> getCategories(Context context,
|
||||
Map<Pair<String, String>, Tile> cache) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final boolean setup =
|
||||
Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
|
||||
final ArrayList<Tile> tiles = new ArrayList<>();
|
||||
final UserManager userManager = (UserManager) context.getSystemService(
|
||||
Context.USER_SERVICE);
|
||||
for (UserHandle user : userManager.getUserProfiles()) {
|
||||
// TODO: Needs much optimization, too many PM queries going on here.
|
||||
if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
|
||||
// Only add Settings for this user.
|
||||
loadTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true);
|
||||
loadTilesForAction(context, user, OPERATOR_SETTINGS, cache,
|
||||
OPERATOR_DEFAULT_CATEGORY, tiles, false);
|
||||
loadTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
|
||||
MANUFACTURER_DEFAULT_CATEGORY, tiles, false);
|
||||
}
|
||||
if (setup) {
|
||||
loadTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
|
||||
loadTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false);
|
||||
}
|
||||
}
|
||||
|
||||
final HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
|
||||
for (Tile tile : tiles) {
|
||||
final String categoryKey = tile.getCategory();
|
||||
DashboardCategory category = categoryMap.get(categoryKey);
|
||||
if (category == null) {
|
||||
category = new DashboardCategory(categoryKey);
|
||||
|
||||
if (category == null) {
|
||||
Log.w(LOG_TAG, "Couldn't find category " + categoryKey);
|
||||
continue;
|
||||
}
|
||||
categoryMap.put(categoryKey, category);
|
||||
}
|
||||
category.addTile(tile);
|
||||
}
|
||||
final ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
|
||||
for (DashboardCategory category : categories) {
|
||||
category.sortTiles();
|
||||
}
|
||||
|
||||
if (DEBUG_TIMING) {
|
||||
Log.d(LOG_TAG, "getCategories took "
|
||||
+ (System.currentTimeMillis() - startTime) + " ms");
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void loadTilesForAction(Context context,
|
||||
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
|
||||
String defaultCategory, List<Tile> outTiles, boolean requireSettings) {
|
||||
final Intent intent = new Intent(action);
|
||||
if (requireSettings) {
|
||||
intent.setPackage(SETTING_PKG);
|
||||
}
|
||||
loadActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent);
|
||||
loadProviderTiles(context, user, addedCache, defaultCategory, outTiles, intent);
|
||||
}
|
||||
|
||||
private static void loadActivityTiles(Context context,
|
||||
UserHandle user, Map<Pair<String, String>, Tile> addedCache,
|
||||
String defaultCategory, List<Tile> outTiles, Intent intent) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
|
||||
PackageManager.GET_META_DATA, user.getIdentifier());
|
||||
for (ResolveInfo resolved : results) {
|
||||
if (!resolved.system) {
|
||||
// Do not allow any app to add to settings, only system ones.
|
||||
continue;
|
||||
}
|
||||
final ActivityInfo activityInfo = resolved.activityInfo;
|
||||
final Bundle metaData = activityInfo.metaData;
|
||||
loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadProviderTiles(Context context,
|
||||
UserHandle user, Map<Pair<String, String>, Tile> addedCache,
|
||||
String defaultCategory, List<Tile> outTiles, Intent intent) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final List<ResolveInfo> results = pm.queryIntentContentProvidersAsUser(intent,
|
||||
0 /* flags */, user.getIdentifier());
|
||||
for (ResolveInfo resolved : results) {
|
||||
if (!resolved.system) {
|
||||
// Do not allow any app to add to settings, only system ones.
|
||||
continue;
|
||||
}
|
||||
final ProviderInfo providerInfo = resolved.providerInfo;
|
||||
final List<Bundle> entryData = getEntryDataFromProvider(
|
||||
// Build new context so the entry data is retrieved for the queried user.
|
||||
context.createContextAsUser(user, 0 /* flags */),
|
||||
providerInfo.authority);
|
||||
if (entryData == null || entryData.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (Bundle metaData : entryData) {
|
||||
loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData,
|
||||
providerInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache,
|
||||
String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData,
|
||||
ComponentInfo componentInfo) {
|
||||
// Skip loading tile if the component is tagged primary_profile_only but not running on
|
||||
// the current user.
|
||||
if (user.getIdentifier() != ActivityManager.getCurrentUser()
|
||||
&& Tile.isPrimaryProfileOnly(componentInfo.metaData)) {
|
||||
Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "
|
||||
+ intent + " is primary profile only, skip loading tile for uid "
|
||||
+ user.getIdentifier());
|
||||
return;
|
||||
}
|
||||
|
||||
String categoryKey = defaultCategory;
|
||||
// Load category
|
||||
if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
|
||||
&& categoryKey == null) {
|
||||
Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "
|
||||
+ intent + " missing metadata "
|
||||
+ (metaData == null ? "" : EXTRA_CATEGORY_KEY));
|
||||
return;
|
||||
} else {
|
||||
categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
|
||||
}
|
||||
|
||||
final boolean isProvider = componentInfo instanceof ProviderInfo;
|
||||
final Pair<String, String> key = isProvider
|
||||
? new Pair<>(((ProviderInfo) componentInfo).authority,
|
||||
metaData.getString(META_DATA_PREFERENCE_KEYHINT))
|
||||
: new Pair<>(componentInfo.packageName, componentInfo.name);
|
||||
Tile tile = addedCache.get(key);
|
||||
if (tile == null) {
|
||||
tile = isProvider
|
||||
? new ProviderTile((ProviderInfo) componentInfo, categoryKey, metaData)
|
||||
: new ActivityTile((ActivityInfo) componentInfo, categoryKey);
|
||||
addedCache.put(key, tile);
|
||||
} else {
|
||||
tile.setMetaData(metaData);
|
||||
}
|
||||
|
||||
if (!tile.userHandle.contains(user)) {
|
||||
tile.userHandle.add(user);
|
||||
}
|
||||
if (metaData.containsKey(META_DATA_PREFERENCE_PENDING_INTENT)) {
|
||||
tile.pendingIntentMap.put(
|
||||
user, metaData.getParcelable(META_DATA_PREFERENCE_PENDING_INTENT));
|
||||
}
|
||||
if (!outTiles.contains(tile)) {
|
||||
outTiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the entry data of the key specified from the provider */
|
||||
// TODO(b/144732809): rearrange methods by access level modifiers
|
||||
static Bundle getEntryDataFromProvider(Context context, String authority, String key) {
|
||||
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
|
||||
final Uri uri = buildUri(authority, EntriesProvider.METHOD_GET_ENTRY_DATA, key);
|
||||
Bundle result = getBundleFromUri(context, uri, providerMap, null /* bundle */);
|
||||
if (result == null) {
|
||||
Uri fallbackUri = buildUri(authority, EntriesProvider.METHOD_GET_SWITCH_DATA, key);
|
||||
result = getBundleFromUri(context, fallbackUri, providerMap, null /* bundle */);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns all entry data from the provider */
|
||||
private static List<Bundle> getEntryDataFromProvider(Context context, String authority) {
|
||||
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
|
||||
final Uri uri = buildUri(authority, EntriesProvider.METHOD_GET_ENTRY_DATA);
|
||||
final Bundle result = getBundleFromUri(context, uri, providerMap, null /* bundle */);
|
||||
if (result != null) {
|
||||
return result.getParcelableArrayList(EntriesProvider.EXTRA_ENTRY_DATA);
|
||||
} else {
|
||||
Uri fallbackUri = buildUri(authority, EntriesProvider.METHOD_GET_SWITCH_DATA);
|
||||
Bundle fallbackResult =
|
||||
getBundleFromUri(context, fallbackUri, providerMap, null /* bundle */);
|
||||
return fallbackResult != null
|
||||
? fallbackResult.getParcelableArrayList(EntriesProvider.EXTRA_SWITCH_DATA)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete uri from the meta data key of the tile.
|
||||
*
|
||||
* A complete uri should contain at least one path segment and be one of the following types:
|
||||
* content://authority/method
|
||||
* content://authority/method/key
|
||||
*
|
||||
* If the uri from the tile is not complete, build a uri by the default method and the
|
||||
* preference key.
|
||||
*
|
||||
* @param tile Tile which contains meta data
|
||||
* @param metaDataKey Key mapping to the uri in meta data
|
||||
* @param defaultMethod Method to be attached to the uri by default if it has no path segment
|
||||
* @return Uri associated with the key
|
||||
*/
|
||||
public static Uri getCompleteUri(Tile tile, String metaDataKey, String defaultMethod) {
|
||||
final String uriString = tile.getMetaData().getString(metaDataKey);
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Uri uri = Uri.parse(uriString);
|
||||
final List<String> pathSegments = uri.getPathSegments();
|
||||
if (pathSegments != null && !pathSegments.isEmpty()) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
final String key = tile.getMetaData().getString(META_DATA_PREFERENCE_KEYHINT);
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
Log.w(LOG_TAG, "Please specify the meta-data " + META_DATA_PREFERENCE_KEYHINT
|
||||
+ " in AndroidManifest.xml for " + uriString);
|
||||
return buildUri(uri.getAuthority(), defaultMethod);
|
||||
}
|
||||
return buildUri(uri.getAuthority(), defaultMethod, key);
|
||||
}
|
||||
|
||||
static Uri buildUri(String authority, String method, String key) {
|
||||
return new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(authority)
|
||||
.appendPath(method)
|
||||
.appendPath(key)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Uri buildUri(String authority, String method) {
|
||||
return new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(authority)
|
||||
.appendPath(method)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon package name and resource id from content provider.
|
||||
*
|
||||
* @param context context
|
||||
* @param packageName package name of the target activity
|
||||
* @param uri URI for the content provider
|
||||
* @param providerMap Maps URI authorities to providers
|
||||
* @return package name and resource id of the icon specified
|
||||
*/
|
||||
public static Pair<String, Integer> getIconFromUri(Context context, String packageName,
|
||||
Uri uri, Map<String, IContentProvider> providerMap) {
|
||||
final Bundle bundle = getBundleFromUri(context, uri, providerMap, null /* bundle */);
|
||||
if (bundle == null) {
|
||||
return null;
|
||||
}
|
||||
final String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
|
||||
if (TextUtils.isEmpty(iconPackageName)) {
|
||||
return null;
|
||||
}
|
||||
int resId = bundle.getInt(META_DATA_PREFERENCE_ICON, 0);
|
||||
if (resId == 0) {
|
||||
return null;
|
||||
}
|
||||
// Icon can either come from the target package or from the Settings app.
|
||||
if (iconPackageName.equals(packageName)
|
||||
|| iconPackageName.equals(context.getPackageName())) {
|
||||
return Pair.create(iconPackageName, resId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets text associated with the input key from the content provider.
|
||||
*
|
||||
* @param context context
|
||||
* @param uri URI for the content provider
|
||||
* @param providerMap Maps URI authorities to providers
|
||||
* @param key Key mapping to the text in bundle returned by the content provider
|
||||
* @return Text associated with the key, if returned by the content provider
|
||||
*/
|
||||
public static String getTextFromUri(Context context, Uri uri,
|
||||
Map<String, IContentProvider> providerMap, String key) {
|
||||
final Bundle bundle = getBundleFromUri(context, uri, providerMap, null /* bundle */);
|
||||
return (bundle != null) ? bundle.getString(key) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets boolean associated with the input key from the content provider.
|
||||
*
|
||||
* @param context context
|
||||
* @param uri URI for the content provider
|
||||
* @param providerMap Maps URI authorities to providers
|
||||
* @param key Key mapping to the text in bundle returned by the content provider
|
||||
* @return Boolean associated with the key, if returned by the content provider
|
||||
*/
|
||||
public static boolean getBooleanFromUri(Context context, Uri uri,
|
||||
Map<String, IContentProvider> providerMap, String key) {
|
||||
final Bundle bundle = getBundleFromUri(context, uri, providerMap, null /* bundle */);
|
||||
return (bundle != null) ? bundle.getBoolean(key) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts boolean associated with the input key to the content provider.
|
||||
*
|
||||
* @param context context
|
||||
* @param uri URI for the content provider
|
||||
* @param providerMap Maps URI authorities to providers
|
||||
* @param key Key mapping to the text in bundle returned by the content provider
|
||||
* @param value Boolean associated with the key
|
||||
* @return Bundle associated with the action, if returned by the content provider
|
||||
*/
|
||||
public static Bundle putBooleanToUriAndGetResult(Context context, Uri uri,
|
||||
Map<String, IContentProvider> providerMap, String key, boolean value) {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(key, value);
|
||||
return getBundleFromUri(context, uri, providerMap, bundle);
|
||||
}
|
||||
|
||||
private static Bundle getBundleFromUri(Context context, Uri uri,
|
||||
Map<String, IContentProvider> providerMap, Bundle bundle) {
|
||||
final Pair<String, String> args = getMethodAndKey(uri);
|
||||
if (args == null) {
|
||||
return null;
|
||||
}
|
||||
final String method = args.first;
|
||||
final String key = args.second;
|
||||
if (TextUtils.isEmpty(method)) {
|
||||
return null;
|
||||
}
|
||||
final IContentProvider provider = getProviderFromUri(context, uri, providerMap);
|
||||
if (provider == null) {
|
||||
return null;
|
||||
}
|
||||
if (!TextUtils.isEmpty(key)) {
|
||||
if (bundle == null) {
|
||||
bundle = new Bundle();
|
||||
}
|
||||
bundle.putString(META_DATA_PREFERENCE_KEYHINT, key);
|
||||
}
|
||||
try {
|
||||
return provider.call(context.getAttributionSource(),
|
||||
uri.getAuthority(), method, uri.toString(), bundle);
|
||||
} catch (RemoteException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IContentProvider getProviderFromUri(Context context, Uri uri,
|
||||
Map<String, IContentProvider> providerMap) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
final String authority = uri.getAuthority();
|
||||
if (TextUtils.isEmpty(authority)) {
|
||||
return null;
|
||||
}
|
||||
if (!providerMap.containsKey(authority)) {
|
||||
providerMap.put(authority, context.getContentResolver().acquireUnstableProvider(uri));
|
||||
}
|
||||
return providerMap.get(authority);
|
||||
}
|
||||
|
||||
/** Returns method and key of the complete uri. */
|
||||
private static Pair<String, String> getMethodAndKey(Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
final List<String> pathSegments = uri.getPathSegments();
|
||||
if (pathSegments == null || pathSegments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final String method = pathSegments.get(0);
|
||||
final String key = pathSegments.size() > 1 ? pathSegments.get(1) : null;
|
||||
return Pair.create(method, key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user