Snap for 4977457 from a60d914d5f to qt-release
Change-Id: Id50a0d62d65f396538098278f4eabea74a267201
This commit is contained in:
@@ -26,5 +26,4 @@
|
||||
android:paddingEnd="@dimen/dashboard_padding_end"
|
||||
android:paddingTop="@dimen/dashboard_padding_top"
|
||||
android:paddingBottom="@dimen/dashboard_padding_bottom"
|
||||
android:scrollbars="vertical"/>
|
||||
|
||||
android:scrollbars="vertical"/>
|
||||
@@ -10071,4 +10071,11 @@
|
||||
|
||||
<!-- Title for Connected device shortcut [CHAR LIMIT=30] -->
|
||||
<string name="devices_title">Devices</string>
|
||||
|
||||
<!-- UI debug setting: Enable High Refresh Rate virtual panel [CHAR LIMIT=25] -->
|
||||
<string name="high_frequency_display_device_title">High Frequency Panel</string>
|
||||
|
||||
<!-- UI debug setting: Enable High Refresh Rate virtual panel [CHAR LIMIT=50] -->
|
||||
<string name="high_frequency_display_device_summary">Enable Virtual High Frequency Panel</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -34,18 +34,18 @@
|
||||
android:title="@string/applications_settings"
|
||||
android:key="all_app_info"
|
||||
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
|
||||
android:order="20" />
|
||||
android:order="20"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<!-- Empty category to draw divider -->
|
||||
<PreferenceCategory
|
||||
android:key="all_app_info_divider"
|
||||
android:order="-190" />
|
||||
android:order="-190"/>
|
||||
|
||||
<!-- Notifications (appears before manage_perms), default apps (appears after) -->
|
||||
<PreferenceCategory
|
||||
android:key="dashboard_tile_placeholder"
|
||||
android:order="10" />
|
||||
android:order="10"/>
|
||||
|
||||
<Preference
|
||||
android:key="manage_perms"
|
||||
@@ -53,7 +53,7 @@
|
||||
android:order="12"
|
||||
settings:keywords="@string/keywords_app_permissions"
|
||||
settings:controller="com.android.settings.applications.AppPermissionsPreferenceController">
|
||||
<intent android:action="android.intent.action.MANAGE_PERMISSIONS" />
|
||||
<intent android:action="android.intent.action.MANAGE_PERMISSIONS"/>
|
||||
</Preference>
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
@@ -64,7 +64,7 @@
|
||||
<intent
|
||||
android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.android.cellbroadcastreceiver"
|
||||
android:targetClass="com.android.cellbroadcastreceiver.CellBroadcastSettings" />
|
||||
android:targetClass="com.android.cellbroadcastreceiver.CellBroadcastSettings"/>
|
||||
</com.android.settingslib.RestrictedPreference>
|
||||
|
||||
<Preference
|
||||
@@ -72,6 +72,6 @@
|
||||
android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
|
||||
android:title="@string/special_access"
|
||||
android:order="20"
|
||||
settings:searchable="false"/>
|
||||
settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -359,6 +359,11 @@
|
||||
android:entries="@array/overlay_display_devices_entries"
|
||||
android:entryValues="@array/overlay_display_devices_values" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="high_frequency_display_device"
|
||||
android:title="@string/high_frequency_display_device_title"
|
||||
android:summary="@string/high_frequency_display_device_summary" />
|
||||
|
||||
<com.android.settings.display.DensityPreference
|
||||
android:key="density"
|
||||
android:title="@string/developer_smallest_width" />
|
||||
|
||||
@@ -42,10 +42,6 @@
|
||||
settings:keywords="@string/keywords_more_mobile_networks"
|
||||
settings:userRestriction="no_config_mobile_networks"
|
||||
settings:useAdminDisabledSummary="true">
|
||||
<intent
|
||||
android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="com.android.phone"
|
||||
android:targetClass="com.android.phone.MobileNetworkSettings" />
|
||||
</com.android.settingslib.RestrictedPreference>
|
||||
|
||||
<com.android.settingslib.RestrictedPreference
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.provider.SearchIndexableResource;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
@@ -33,8 +35,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
@SearchIndexable
|
||||
public class AppAndNotificationDashboardFragment extends DashboardFragment {
|
||||
|
||||
@@ -60,6 +60,12 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment {
|
||||
return R.xml.app_and_notification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final Activity activity = getActivity();
|
||||
@@ -77,7 +83,6 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
controllers.add(new EmergencyBroadcastPreferenceController(context,
|
||||
"app_and_notif_cell_broadcast_settings"));
|
||||
controllers.add(new SpecialAppAccessPreferenceController(context));
|
||||
controllers.add(new RecentAppsPreferenceController(context, app, host));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class AppStateBaseBridge implements ApplicationsState.Callbacks
|
||||
// the same time as us as well.
|
||||
mHandler = new BackgroundHandler(mAppState != null ? mAppState.getBackgroundLooper()
|
||||
: Looper.getMainLooper());
|
||||
mMainHandler = new MainHandler();
|
||||
mMainHandler = new MainHandler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
@@ -106,11 +106,16 @@ public abstract class AppStateBaseBridge implements ApplicationsState.Callbacks
|
||||
}
|
||||
|
||||
protected abstract void loadAllExtraInfo();
|
||||
|
||||
protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
|
||||
|
||||
private class MainHandler extends Handler {
|
||||
private static final int MSG_INFO_UPDATED = 1;
|
||||
|
||||
public MainHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
|
||||
@@ -13,43 +13,140 @@
|
||||
*/
|
||||
package com.android.settings.applications;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.datausage.DataSaverBackend;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
public class SpecialAppAccessPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin {
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.datausage.AppStateDataUsageBridge;
|
||||
import com.android.settings.datausage.DataSaverBackend;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
|
||||
private static final String KEY_SPECIAL_ACCESS = "special_access";
|
||||
import java.util.ArrayList;
|
||||
|
||||
private DataSaverBackend mDataSaverBackend;
|
||||
public class SpecialAppAccessPreferenceController extends BasePreferenceController implements
|
||||
AppStateBaseBridge.Callback, ApplicationsState.Callbacks, LifecycleObserver, OnStart,
|
||||
OnStop, OnDestroy {
|
||||
|
||||
public SpecialAppAccessPreferenceController(Context context) {
|
||||
super(context);
|
||||
@VisibleForTesting
|
||||
ApplicationsState.Session mSession;
|
||||
|
||||
private final ApplicationsState mApplicationsState;
|
||||
private final AppStateDataUsageBridge mDataUsageBridge;
|
||||
private final DataSaverBackend mDataSaverBackend;
|
||||
|
||||
private Preference mPreference;
|
||||
private boolean mExtraLoaded;
|
||||
|
||||
|
||||
public SpecialAppAccessPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mApplicationsState = ApplicationsState.getInstance(
|
||||
(Application) context.getApplicationContext());
|
||||
mDataSaverBackend = new DataSaverBackend(context);
|
||||
mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
|
||||
}
|
||||
|
||||
public void setSession(Lifecycle lifecycle) {
|
||||
mSession = mApplicationsState.newSession(this, lifecycle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE_UNSEARCHABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY_SPECIAL_ACCESS;
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mDataUsageBridge.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mDataUsageBridge.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mDataUsageBridge.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
if (mDataSaverBackend == null) {
|
||||
mDataSaverBackend = new DataSaverBackend(mContext);
|
||||
}
|
||||
final int count = mDataSaverBackend.getWhitelistedCount();
|
||||
preference.setSummary(mContext.getResources().getQuantityString(
|
||||
R.plurals.special_access_summary, count, count));
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExtraInfoUpdated() {
|
||||
mExtraLoaded = true;
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
private void updateSummary() {
|
||||
if (!mExtraLoaded || mPreference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ArrayList<ApplicationsState.AppEntry> allApps = mSession.getAllApps();
|
||||
int count = 0;
|
||||
for (ApplicationsState.AppEntry entry : allApps) {
|
||||
if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.extraInfo != null && ((AppStateDataUsageBridge.DataUsageState)
|
||||
entry.extraInfo).isDataSaverWhitelisted) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
mPreference.setSummary(mContext.getResources().getQuantityString(
|
||||
R.plurals.special_access_summary, count, count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningStateChanged(boolean running) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageListChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageIconChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageSizeChanged(String packageName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllSizesComputed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLauncherInfoChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadEntriesCompleted() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class DefaultHomePreferenceController extends DefaultAppPreferenceControl
|
||||
Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
|
||||
.setPackage(packageName)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return mPackageManager.queryIntentActivities(intent, 0).size() == 1 ? intent : null;
|
||||
return intent.resolveActivity(mPackageManager) != null ? intent : null;
|
||||
}
|
||||
|
||||
public static boolean hasHomePreference(String pkg, Context context) {
|
||||
|
||||
@@ -98,11 +98,11 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
BluetoothDevice device = mCachedDevice.getDevice();
|
||||
profilePref.setEnabled(!mCachedDevice.isBusy());
|
||||
if (profile instanceof MapProfile) {
|
||||
profilePref.setChecked(mCachedDevice.getMessagePermissionChoice()
|
||||
== CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||
profilePref.setChecked(device.getMessageAccessPermission()
|
||||
== BluetoothDevice.ACCESS_ALLOWED);
|
||||
} else if (profile instanceof PbapServerProfile) {
|
||||
profilePref.setChecked(mCachedDevice.getPhonebookPermissionChoice()
|
||||
== CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||
profilePref.setChecked(device.getPhonebookAccessPermission()
|
||||
== BluetoothDevice.ACCESS_ALLOWED);
|
||||
} else if (profile instanceof PanProfile) {
|
||||
profilePref.setChecked(profile.getConnectionStatus(device) ==
|
||||
BluetoothProfile.STATE_CONNECTED);
|
||||
@@ -130,31 +130,31 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
/**
|
||||
* Helper method to enable a profile for a device.
|
||||
*/
|
||||
private void enableProfile(LocalBluetoothProfile profile, BluetoothDevice device,
|
||||
SwitchPreference profilePref) {
|
||||
private void enableProfile(LocalBluetoothProfile profile) {
|
||||
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
|
||||
if (profile instanceof PbapServerProfile) {
|
||||
mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||
bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
// We don't need to do the additional steps below for this profile.
|
||||
return;
|
||||
}
|
||||
if (profile instanceof MapProfile) {
|
||||
mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
|
||||
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
profile.setPreferred(device, true);
|
||||
profile.setPreferred(bluetoothDevice, true);
|
||||
mCachedDevice.connectProfile(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to disable a profile for a device
|
||||
*/
|
||||
private void disableProfile(LocalBluetoothProfile profile, BluetoothDevice device,
|
||||
SwitchPreference profilePref) {
|
||||
private void disableProfile(LocalBluetoothProfile profile) {
|
||||
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
|
||||
mCachedDevice.disconnect(profile);
|
||||
profile.setPreferred(device, false);
|
||||
profile.setPreferred(bluetoothDevice, false);
|
||||
if (profile instanceof MapProfile) {
|
||||
mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
|
||||
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
} else if (profile instanceof PbapServerProfile) {
|
||||
mCachedDevice.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
|
||||
bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,11 +175,10 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
}
|
||||
}
|
||||
SwitchPreference profilePref = (SwitchPreference) preference;
|
||||
BluetoothDevice device = mCachedDevice.getDevice();
|
||||
if (profilePref.isChecked()) {
|
||||
enableProfile(profile, device, profilePref);
|
||||
enableProfile(profile);
|
||||
} else {
|
||||
disableProfile(profile, device, profilePref);
|
||||
disableProfile(profile);
|
||||
}
|
||||
refreshProfilePreference(profilePref, profile);
|
||||
return true;
|
||||
@@ -191,17 +190,18 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
|
||||
*/
|
||||
private List<LocalBluetoothProfile> getProfiles() {
|
||||
List<LocalBluetoothProfile> result = mCachedDevice.getConnectableProfiles();
|
||||
final BluetoothDevice device = mCachedDevice.getDevice();
|
||||
|
||||
final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
|
||||
final int pbapPermission = device.getPhonebookAccessPermission();
|
||||
// Only provide PBAP cabability if the client device has requested PBAP.
|
||||
if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
|
||||
if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
||||
final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
|
||||
result.add(psp);
|
||||
}
|
||||
|
||||
final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
|
||||
final int mapPermission = mCachedDevice.getMessagePermissionChoice();
|
||||
if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
|
||||
final int mapPermission = device.getMessageAccessPermission();
|
||||
if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
|
||||
result.add(mapProfile);
|
||||
}
|
||||
|
||||
|
||||
@@ -237,42 +237,42 @@ public final class BluetoothPermissionRequest extends BroadcastReceiver {
|
||||
String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY;
|
||||
|
||||
if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
|
||||
int phonebookPermission = cachedDevice.getPhonebookPermissionChoice();
|
||||
int phonebookPermission = mDevice.getPhonebookAccessPermission();
|
||||
|
||||
if (phonebookPermission == CachedBluetoothDevice.ACCESS_UNKNOWN) {
|
||||
if (phonebookPermission == BluetoothDevice.ACCESS_UNKNOWN) {
|
||||
// Leave 'processed' as false.
|
||||
} else if (phonebookPermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
|
||||
} else if (phonebookPermission == BluetoothDevice.ACCESS_ALLOWED) {
|
||||
sendReplyIntentToReceiver(true);
|
||||
processed = true;
|
||||
} else if (phonebookPermission == CachedBluetoothDevice.ACCESS_REJECTED) {
|
||||
} else if (phonebookPermission == BluetoothDevice.ACCESS_REJECTED) {
|
||||
sendReplyIntentToReceiver(false);
|
||||
processed = true;
|
||||
} else {
|
||||
Log.e(TAG, "Bad phonebookPermission: " + phonebookPermission);
|
||||
}
|
||||
} else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
|
||||
int messagePermission = cachedDevice.getMessagePermissionChoice();
|
||||
int messagePermission = mDevice.getMessageAccessPermission();
|
||||
|
||||
if (messagePermission == CachedBluetoothDevice.ACCESS_UNKNOWN) {
|
||||
if (messagePermission == BluetoothDevice.ACCESS_UNKNOWN) {
|
||||
// Leave 'processed' as false.
|
||||
} else if (messagePermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
|
||||
} else if (messagePermission == BluetoothDevice.ACCESS_ALLOWED) {
|
||||
sendReplyIntentToReceiver(true);
|
||||
processed = true;
|
||||
} else if (messagePermission == CachedBluetoothDevice.ACCESS_REJECTED) {
|
||||
} else if (messagePermission == BluetoothDevice.ACCESS_REJECTED) {
|
||||
sendReplyIntentToReceiver(false);
|
||||
processed = true;
|
||||
} else {
|
||||
Log.e(TAG, "Bad messagePermission: " + messagePermission);
|
||||
}
|
||||
} else if(mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) {
|
||||
int simPermission = cachedDevice.getSimPermissionChoice();
|
||||
int simPermission = mDevice.getSimAccessPermission();
|
||||
|
||||
if (simPermission == CachedBluetoothDevice.ACCESS_UNKNOWN) {
|
||||
if (simPermission == BluetoothDevice.ACCESS_UNKNOWN) {
|
||||
// Leave 'processed' as false.
|
||||
} else if (simPermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
|
||||
} else if (simPermission == BluetoothDevice.ACCESS_ALLOWED) {
|
||||
sendReplyIntentToReceiver(true);
|
||||
processed = true;
|
||||
} else if (simPermission == CachedBluetoothDevice.ACCESS_REJECTED) {
|
||||
} else if (simPermission == BluetoothDevice.ACCESS_REJECTED) {
|
||||
sendReplyIntentToReceiver(false);
|
||||
processed = true;
|
||||
} else {
|
||||
|
||||
@@ -25,4 +25,5 @@ public class FeatureFlags {
|
||||
public static final String AUDIO_SWITCHER_SETTINGS = "settings_audio_switcher";
|
||||
public static final String DYNAMIC_HOMEPAGE = "settings_dynamic_homepage";
|
||||
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
|
||||
public static final String MOBILE_NETWORK_V2 = "settings_mobile_network_v2";
|
||||
}
|
||||
|
||||
@@ -95,19 +95,10 @@ public class DataSaverBackend {
|
||||
return mUidPolicies.get(uid, POLICY_NONE) == POLICY_ALLOW_METERED_BACKGROUND;
|
||||
}
|
||||
|
||||
public int getWhitelistedCount() {
|
||||
int count = 0;
|
||||
loadWhitelist();
|
||||
for (int i = 0; i < mUidPolicies.size(); i++) {
|
||||
if (mUidPolicies.valueAt(i) == POLICY_ALLOW_METERED_BACKGROUND) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void loadWhitelist() {
|
||||
if (mWhitelistInitialized) return;
|
||||
if (mWhitelistInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)) {
|
||||
mUidPolicies.put(uid, POLICY_ALLOW_METERED_BACKGROUND);
|
||||
@@ -135,7 +126,9 @@ public class DataSaverBackend {
|
||||
}
|
||||
|
||||
private void loadBlacklist() {
|
||||
if (mBlacklistInitialized) return;
|
||||
if (mBlacklistInitialized) {
|
||||
return;
|
||||
}
|
||||
for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
|
||||
mUidPolicies.put(uid, POLICY_REJECT_METERED_BACKGROUND);
|
||||
}
|
||||
@@ -212,7 +205,9 @@ public class DataSaverBackend {
|
||||
|
||||
public interface Listener {
|
||||
void onDataSaverChanged(boolean isDataSaving);
|
||||
|
||||
void onWhitelistStatusChanged(int uid, boolean isWhitelisted);
|
||||
|
||||
void onBlacklistStatusChanged(int uid, boolean isBlacklisted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
controllers.add(new TransitionAnimationScalePreferenceController(context));
|
||||
controllers.add(new AnimatorDurationScalePreferenceController(context));
|
||||
controllers.add(new SecondaryDisplayPreferenceController(context));
|
||||
controllers.add(new HighFrequencyDisplayPreferenceController(context));
|
||||
controllers.add(new GpuViewUpdatesPreferenceController(context));
|
||||
controllers.add(new HardwareLayersUpdatesPreferenceController(context));
|
||||
controllers.add(new DebugGpuOverdrawPreferenceController(context));
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
public class HighFrequencyDisplayPreferenceController extends DeveloperOptionsPreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
|
||||
|
||||
private static final String HIGH_FREQUENCY_DISPLAY_KEY = "high_frequency_display_device";
|
||||
|
||||
private static final String SURFACE_FLINGER_SERVICE_KEY = "SurfaceFlinger";
|
||||
private static final String SURFACE_COMPOSER_INTERFACE_KEY = "android.ui.ISurfaceComposer";
|
||||
private static final int SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE = 1029;
|
||||
|
||||
private final IBinder mSurfaceFlingerBinder;
|
||||
|
||||
public HighFrequencyDisplayPreferenceController(Context context) {
|
||||
super(context);
|
||||
mSurfaceFlingerBinder = ServiceManager.getService(SURFACE_FLINGER_SERVICE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return HIGH_FREQUENCY_DISPLAY_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Boolean isEnabled = (Boolean) newValue;
|
||||
writeHighFrequencyDisplaySetting(isEnabled);
|
||||
((SwitchPreference) preference).setChecked(isEnabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
boolean enableHighFrequencyPanel = readHighFrequencyDisplaySetting();
|
||||
((SwitchPreference) preference).setChecked(enableHighFrequencyPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
writeHighFrequencyDisplaySetting(false);
|
||||
((SwitchPreference) mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean readHighFrequencyDisplaySetting() {
|
||||
boolean isEnabled = false;
|
||||
try {
|
||||
if (mSurfaceFlingerBinder != null) {
|
||||
final Parcel data = Parcel.obtain();
|
||||
final Parcel result = Parcel.obtain();
|
||||
data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
|
||||
data.writeInt(0);
|
||||
data.writeInt(0);
|
||||
mSurfaceFlingerBinder.transact(
|
||||
SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE,
|
||||
data, result, 0);
|
||||
|
||||
if (result.readInt() != 1 || result.readInt() != 1) {
|
||||
isEnabled = true;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// intentional no-op
|
||||
}
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeHighFrequencyDisplaySetting(boolean isEnabled) {
|
||||
int multiplier;
|
||||
int divisor;
|
||||
|
||||
if (isEnabled) {
|
||||
// 60Hz * 3/2 = 90Hz
|
||||
multiplier = 2;
|
||||
divisor = 3;
|
||||
} else {
|
||||
multiplier = 1;
|
||||
divisor = 1;
|
||||
}
|
||||
|
||||
try {
|
||||
if (mSurfaceFlingerBinder != null) {
|
||||
final Parcel data = Parcel.obtain();
|
||||
data.writeInterfaceToken(SURFACE_COMPOSER_INTERFACE_KEY);
|
||||
data.writeInt(multiplier);
|
||||
data.writeInt(divisor);
|
||||
mSurfaceFlingerBinder.transact(
|
||||
SURFACE_FLINGER_HIGH_FREQUENCY_DISPLAY_CODE,
|
||||
data, null, 0);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// intentional no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.android.settings.development.autofill;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.autofill.AutofillManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -32,6 +33,7 @@ public final class AutofillLoggingLevelPreferenceController
|
||||
extends DeveloperOptionsPreferenceController
|
||||
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "AutofillLoggingLevelPreferenceController";
|
||||
private static final String AUTOFILL_LOGGING_LEVEL_KEY = "autofill_logging_level";
|
||||
|
||||
private final String[] mListValues;
|
||||
@@ -73,6 +75,12 @@ public final class AutofillLoggingLevelPreferenceController
|
||||
}
|
||||
|
||||
private void updateOptions() {
|
||||
if (mPreference == null) {
|
||||
// TODO: there should be a hook on AbstractPreferenceController where we could
|
||||
// unregister mObserver and avoid this check
|
||||
Log.v(TAG, "ignoring Settings update because UI is gone");
|
||||
return;
|
||||
}
|
||||
final int level = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ public class StorageWizardFormatConfirm extends InstrumentedDialogFragment {
|
||||
|
||||
final StorageWizardFormatConfirm fragment = new StorageWizardFormatConfirm();
|
||||
fragment.setArguments(args);
|
||||
// TODO (b/111150236) : Need to check it again.
|
||||
fragment.show(activity.getSupportFragmentManager(), TAG_FORMAT_WARNING);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
|
||||
setHeaderText(R.string.storage_wizard_format_progress_title, getDiskShortDescription());
|
||||
setBodyText(R.string.storage_wizard_format_progress_body, getDiskDescription());
|
||||
|
||||
// TODO (b/111151113) : Need to check it again.
|
||||
mTask = (PartitionTask) getLastCustomNonConfigurationInstance();
|
||||
if (mTask == null) {
|
||||
mTask = new PartitionTask();
|
||||
@@ -69,7 +68,7 @@ public class StorageWizardFormatProgress extends StorageWizardBase {
|
||||
mTask.setActivity(this);
|
||||
}
|
||||
}
|
||||
// TODO (b/111151113) : Need to check it again.
|
||||
|
||||
@Override
|
||||
public Object onRetainCustomNonConfigurationInstance() {
|
||||
return mTask;
|
||||
|
||||
62
src/com/android/settings/homepage/CardContentLoader.java
Normal file
62
src/com/android/settings/homepage/CardContentLoader.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settingslib.utils.AsyncLoaderCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//TODO(b/112521307): Implement this to make it work with the card database.
|
||||
public class CardContentLoader {
|
||||
|
||||
private static final String TAG = "CardContentLoader";
|
||||
|
||||
private CardContentLoaderListener mListener;
|
||||
|
||||
public interface CardContentLoaderListener {
|
||||
void onFinishCardLoading(List<HomepageCard> homepageCards);
|
||||
}
|
||||
|
||||
public CardContentLoader() {
|
||||
}
|
||||
|
||||
void setListener(CardContentLoaderListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private static class CardLoader extends AsyncLoaderCompat<List<HomepageCard>> {
|
||||
|
||||
public CardLoader(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscardResult(List<HomepageCard> result) {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<HomepageCard> loadInBackground() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.collection.ArraySet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This is a fragment scoped singleton holding a set of {@link HomepageCardController} and
|
||||
* {@link HomepageCardRenderer}.
|
||||
*/
|
||||
public class ControllerRendererPool {
|
||||
|
||||
private static final String TAG = "ControllerRendererPool";
|
||||
|
||||
private final Set<HomepageCardController> mControllers;
|
||||
private final Set<HomepageCardRenderer> mRenderers;
|
||||
|
||||
public ControllerRendererPool() {
|
||||
mControllers = new ArraySet<>();
|
||||
mRenderers = new ArraySet<>();
|
||||
}
|
||||
|
||||
public <T extends HomepageCardController> T getController(Context context,
|
||||
@HomepageCard.CardType int cardType) {
|
||||
final Class<? extends HomepageCardController> clz =
|
||||
HomepageCardLookupTable.getCardControllerClass(cardType);
|
||||
for (HomepageCardController controller : mControllers) {
|
||||
if (controller.getClass() == clz) {
|
||||
Log.d(TAG, "Controller is already there.");
|
||||
return (T) controller;
|
||||
}
|
||||
}
|
||||
|
||||
final HomepageCardController controller = createCardController(context, clz);
|
||||
if (controller != null) {
|
||||
mControllers.add(controller);
|
||||
}
|
||||
return (T) controller;
|
||||
}
|
||||
|
||||
public Set<HomepageCardController> getControllers() {
|
||||
return mControllers;
|
||||
}
|
||||
|
||||
public HomepageCardRenderer getRenderer(Context context, @HomepageCard.CardType int cardType) {
|
||||
final Class<? extends HomepageCardRenderer> clz =
|
||||
HomepageCardLookupTable.getCardRendererClasses(cardType);
|
||||
for (HomepageCardRenderer renderer : mRenderers) {
|
||||
if (renderer.getClass() == clz) {
|
||||
Log.d(TAG, "Renderer is already there.");
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
final HomepageCardRenderer renderer = createCardRenderer(context, clz);
|
||||
if (renderer != null) {
|
||||
mRenderers.add(renderer);
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private HomepageCardController createCardController(Context context,
|
||||
Class<? extends HomepageCardController> clz) {
|
||||
/*
|
||||
if (ConditionHomepageCardController.class == clz) {
|
||||
return new ConditionHomepageCardController(context);
|
||||
}
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
private HomepageCardRenderer createCardRenderer(Context context, Class<?> clz) {
|
||||
//if (ConditionHomepageCardRenderer.class == clz) {
|
||||
// return new ConditionHomepageCardRenderer(context, this /*controllerRendererPool*/);
|
||||
//}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
96
src/com/android/settings/homepage/HomepageAdapter.java
Normal file
96
src/com/android/settings/homepage/HomepageAdapter.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HomepageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
|
||||
HomepageCardUpdateListener {
|
||||
|
||||
private static final String TAG = "HomepageAdapter";
|
||||
|
||||
private final Context mContext;
|
||||
private final ControllerRendererPool mControllerRendererPool;
|
||||
|
||||
private List<HomepageCard> mHomepageCards;
|
||||
private RecyclerView mRecyclerView;
|
||||
|
||||
public HomepageAdapter(Context context, HomepageManager manager) {
|
||||
mContext = context;
|
||||
mHomepageCards = new ArrayList<>();
|
||||
mControllerRendererPool = manager.getControllerRendererPool();
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mHomepageCards.get(position).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return mHomepageCards.get(position).getCardType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int cardType) {
|
||||
final HomepageCardRenderer renderer = mControllerRendererPool.getRenderer(mContext, cardType);
|
||||
final int viewType = renderer.getViewType();
|
||||
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
||||
|
||||
return renderer.createViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
final int cardType = mHomepageCards.get(position).getCardType();
|
||||
final HomepageCardRenderer renderer = mControllerRendererPool.getRenderer(mContext, cardType);
|
||||
|
||||
renderer.bindView(holder, mHomepageCards.get(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mHomepageCards.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView);
|
||||
mRecyclerView = recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHomepageCardUpdated(int cardType, List<HomepageCard> homepageCards) {
|
||||
//TODO(b/112245748): Should implement a DiffCallback so we can use notifyItemChanged()
|
||||
// instead.
|
||||
if (homepageCards == null) {
|
||||
mHomepageCards.clear();
|
||||
} else {
|
||||
mHomepageCards = homepageCards;
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
296
src/com/android/settings/homepage/HomepageCard.java
Normal file
296
src/com/android/settings/homepage/HomepageCard.java
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Data class representing a {@link HomepageCard}.
|
||||
*/
|
||||
public class HomepageCard {
|
||||
|
||||
/**
|
||||
* Flags indicating the type of the HomepageCard.
|
||||
*/
|
||||
@IntDef({CardType.INVALID, CardType.SLICE, CardType.SUGGESTION, CardType.CONDITIONAL})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CardType {
|
||||
int INVALID = -1;
|
||||
int SLICE = 1;
|
||||
int SUGGESTION = 2;
|
||||
int CONDITIONAL = 3;
|
||||
}
|
||||
|
||||
private final String mName;
|
||||
@CardType
|
||||
private final int mCardType;
|
||||
private final double mScore;
|
||||
private final String mSliceUri;
|
||||
private final int mCategory;
|
||||
private final String mLocalizedToLocale;
|
||||
private final String mPackageName;
|
||||
private final String mAppVersion;
|
||||
private final String mTitleResName;
|
||||
private final String mTitleText;
|
||||
private final String mSummaryResName;
|
||||
private final String mSummaryText;
|
||||
private final String mIconResName;
|
||||
private final int mIconResId;
|
||||
private final String mCardAction;
|
||||
private final long mExpireTimeMS;
|
||||
private final Drawable mDrawable;
|
||||
private final boolean mSupportHalfWidth;
|
||||
|
||||
String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
int getCardType() {
|
||||
return mCardType;
|
||||
}
|
||||
|
||||
double getScore() {
|
||||
return mScore;
|
||||
}
|
||||
|
||||
String getTextSliceUri() {
|
||||
return mSliceUri;
|
||||
}
|
||||
|
||||
Uri getSliceUri() {
|
||||
return Uri.parse(mSliceUri);
|
||||
}
|
||||
|
||||
int getCategory() {
|
||||
return mCategory;
|
||||
}
|
||||
|
||||
String getLocalizedToLocale() {
|
||||
return mLocalizedToLocale;
|
||||
}
|
||||
|
||||
String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
String getAppVersion() {
|
||||
return mAppVersion;
|
||||
}
|
||||
|
||||
String getTitleResName() {
|
||||
return mTitleResName;
|
||||
}
|
||||
|
||||
String getTitleText() {
|
||||
return mTitleText;
|
||||
}
|
||||
|
||||
String getSummaryResName() {
|
||||
return mSummaryResName;
|
||||
}
|
||||
|
||||
String getSummaryText() {
|
||||
return mSummaryText;
|
||||
}
|
||||
|
||||
String getIconResName() {
|
||||
return mIconResName;
|
||||
}
|
||||
|
||||
int getIconResId() {
|
||||
return mIconResId;
|
||||
}
|
||||
|
||||
String getCardAction() {
|
||||
return mCardAction;
|
||||
}
|
||||
|
||||
long getExpireTimeMS() {
|
||||
return mExpireTimeMS;
|
||||
}
|
||||
|
||||
Drawable getDrawable() {
|
||||
return mDrawable;
|
||||
}
|
||||
|
||||
boolean getSupportHalfWidth() {
|
||||
return mSupportHalfWidth;
|
||||
}
|
||||
|
||||
HomepageCard(Builder builder) {
|
||||
mName = builder.mName;
|
||||
mCardType = builder.mCardType;
|
||||
mScore = builder.mScore;
|
||||
mSliceUri = builder.mSliceUri;
|
||||
mCategory = builder.mCategory;
|
||||
mLocalizedToLocale = builder.mLocalizedToLocale;
|
||||
mPackageName = builder.mPackageName;
|
||||
mAppVersion = builder.mAppVersion;
|
||||
mTitleResName = builder.mTitleResName;
|
||||
mTitleText = builder.mTitleText;
|
||||
mSummaryResName = builder.mSummaryResName;
|
||||
mSummaryText = builder.mSummaryText;
|
||||
mIconResName = builder.mIconResName;
|
||||
mIconResId = builder.mIconResId;
|
||||
mCardAction = builder.mCardAction;
|
||||
mExpireTimeMS = builder.mExpireTimeMS;
|
||||
mDrawable = builder.mDrawable;
|
||||
mSupportHalfWidth = builder.mSupportHalfWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mName.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that {@link #mName} is treated as a primary key for this class and determines equality.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof HomepageCard)) {
|
||||
return false;
|
||||
}
|
||||
final HomepageCard that = (HomepageCard) obj;
|
||||
|
||||
return TextUtils.equals(mName, that.mName);
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private String mName;
|
||||
private int mCardType;
|
||||
private double mScore;
|
||||
private String mSliceUri;
|
||||
private int mCategory;
|
||||
private String mLocalizedToLocale;
|
||||
private String mPackageName;
|
||||
private String mAppVersion;
|
||||
private String mTitleResName;
|
||||
private String mTitleText;
|
||||
private String mSummaryResName;
|
||||
private String mSummaryText;
|
||||
private String mIconResName;
|
||||
private int mIconResId;
|
||||
private String mCardAction;
|
||||
private long mExpireTimeMS;
|
||||
private Drawable mDrawable;
|
||||
private boolean mSupportHalfWidth;
|
||||
|
||||
public Builder setName(String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCardType(int cardType) {
|
||||
mCardType = cardType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setScore(double score) {
|
||||
mScore = score;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSliceUri(String sliceUri) {
|
||||
mSliceUri = sliceUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCategory(int category) {
|
||||
mCategory = category;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setLocalizedToLocale(String localizedToLocale) {
|
||||
mLocalizedToLocale = localizedToLocale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPackageName(String packageName) {
|
||||
mPackageName = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAppVersion(String appVersion) {
|
||||
mAppVersion = appVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTitleResName(String titleResName) {
|
||||
mTitleResName = titleResName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setTitleText(String titleText) {
|
||||
mTitleText = titleText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSummaryResName(String summaryResName) {
|
||||
mSummaryResName = summaryResName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSummaryText(String summaryText) {
|
||||
mSummaryText = summaryText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setIconResName(String iconResName) {
|
||||
mIconResName = iconResName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setIconResId(int iconResId) {
|
||||
mIconResId = iconResId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCardAction(String cardAction) {
|
||||
mCardAction = cardAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExpireTimeMS(long expireTimeMS) {
|
||||
mExpireTimeMS = expireTimeMS;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDrawable(Drawable drawable) {
|
||||
mDrawable = drawable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSupportHalfWidth(boolean supportHalfWidth) {
|
||||
mSupportHalfWidth = supportHalfWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HomepageCard build() {
|
||||
return new HomepageCard(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//TODO(b/111821137): add test cases
|
||||
/**
|
||||
* Data controller for {@link HomepageCard}.
|
||||
*/
|
||||
public interface HomepageCardController {
|
||||
|
||||
@HomepageCard.CardType
|
||||
int getCardType();
|
||||
|
||||
/**
|
||||
* When data is updated or changed, the new data should be passed to HomepageManager for list
|
||||
* updating.
|
||||
*/
|
||||
void onDataUpdated(List<HomepageCard> cardList);
|
||||
|
||||
void onPrimaryClick(HomepageCard card);
|
||||
|
||||
void onActionClick(HomepageCard card);
|
||||
|
||||
void setLifecycle(Lifecycle lifecycle);
|
||||
|
||||
void setHomepageCardUpdateListener(HomepageCardUpdateListener listener);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import com.android.settings.homepage.HomepageCard.CardType;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class HomepageCardLookupTable {
|
||||
|
||||
static class HomepageMapping implements Comparable<HomepageMapping> {
|
||||
@CardType
|
||||
private final int mCardType;
|
||||
private final Class<? extends HomepageCardController> mControllerClass;
|
||||
private final Class<? extends HomepageCardRenderer> mRendererClass;
|
||||
|
||||
private HomepageMapping(@CardType int cardType,
|
||||
Class<? extends HomepageCardController> controllerClass,
|
||||
Class<? extends HomepageCardRenderer> rendererClass) {
|
||||
mCardType = cardType;
|
||||
mControllerClass = controllerClass;
|
||||
mRendererClass = rendererClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HomepageMapping other) {
|
||||
return Integer.compare(this.mCardType, other.mCardType);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<HomepageMapping> LOOKUP_TABLE = new TreeSet<HomepageMapping>() {
|
||||
{
|
||||
//add(new HomepageMapping(CardType.CONDITIONAL, ConditionHomepageCardController.class,
|
||||
// ConditionHomepageCardRenderer.class));
|
||||
}
|
||||
};
|
||||
|
||||
public static Class<? extends HomepageCardController> getCardControllerClass(
|
||||
@CardType int cardType) {
|
||||
for (HomepageMapping mapping : LOOKUP_TABLE) {
|
||||
if (mapping.mCardType == cardType) {
|
||||
return mapping.mControllerClass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO(b/112578070): Implement multi renderer cases.
|
||||
public static Class<? extends HomepageCardRenderer> getCardRendererClasses(
|
||||
@CardType int cardType) {
|
||||
for (HomepageMapping mapping : LOOKUP_TABLE) {
|
||||
if (mapping.mCardType == cardType) {
|
||||
return mapping.mRendererClass;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
46
src/com/android/settings/homepage/HomepageCardRenderer.java
Normal file
46
src/com/android/settings/homepage/HomepageCardRenderer.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* UI renderer for {@link HomepageCard}.
|
||||
*/
|
||||
public interface HomepageCardRenderer {
|
||||
|
||||
/**
|
||||
* The layout type of the controller.
|
||||
*/
|
||||
int getViewType();
|
||||
|
||||
/**
|
||||
* When {@link HomepageAdapter} calls {@link HomepageAdapter#onCreateViewHolder(ViewGroup,
|
||||
* int)}, this method will be called to retrieve the corresponding
|
||||
* {@link androidx.recyclerview.widget.RecyclerView.ViewHolder}.
|
||||
*/
|
||||
RecyclerView.ViewHolder createViewHolder(View view);
|
||||
|
||||
/**
|
||||
* When {@link HomepageAdapter} calls {@link HomepageAdapter#onBindViewHolder(RecyclerView
|
||||
* .ViewHolder, int)}, this method will be called to bind data to the
|
||||
* {@link androidx.recyclerview.widget.RecyclerView.ViewHolder}.
|
||||
*/
|
||||
void bindView(RecyclerView.ViewHolder holder, HomepageCard card);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* When {@link HomepageCardController} detects changes, it will notify the listeners registered. In
|
||||
* our case, {@link HomepageManager} gets noticed.
|
||||
*
|
||||
* After the list of {@link HomepageCard} gets updated in{@link HomepageManager},
|
||||
* {@link HomepageManager} will notify the listeners registered, {@link HomepageAdapter} in this
|
||||
* case.
|
||||
*/
|
||||
interface HomepageCardUpdateListener {
|
||||
void onHomepageCardUpdated(int cardType, List<HomepageCard> updateList);
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public class HomepageFragment extends InstrumentedFragment {
|
||||
private static final String SAVE_BOTTOM_FRAGMENT_LOADED = "bottom_fragment_loaded";
|
||||
|
||||
private RecyclerView mCardsContainer;
|
||||
private HomepageAdapter mHomepageAdapter;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
|
||||
private FloatingActionButton mSearchButton;
|
||||
@@ -55,6 +56,14 @@ public class HomepageFragment extends InstrumentedFragment {
|
||||
private View mBottomBar;
|
||||
private View mSearchBar;
|
||||
private boolean mBottomFragmentLoaded;
|
||||
private HomepageManager mHomepageManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mHomepageManager = new HomepageManager(getContext(), getSettingsLifecycle());
|
||||
mHomepageManager.startCardContentLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@@ -65,6 +74,9 @@ public class HomepageFragment extends InstrumentedFragment {
|
||||
//TODO(b/111822407): May have to swap to GridLayoutManager
|
||||
mLayoutManager = new LinearLayoutManager(getActivity());
|
||||
mCardsContainer.setLayoutManager(mLayoutManager);
|
||||
mHomepageAdapter = new HomepageAdapter(getContext(), mHomepageManager);
|
||||
mCardsContainer.setAdapter(mHomepageAdapter);
|
||||
mHomepageManager.setListener(mHomepageAdapter);
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
135
src/com/android/settings/homepage/HomepageManager.java
Normal file
135
src/com/android/settings/homepage/HomepageManager.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a centralized manager of multiple {@link HomepageCardController}.
|
||||
*
|
||||
* {@link HomepageManager} first loads data from {@link CardContentLoader} and gets back a list of
|
||||
* {@link HomepageCard}. All subclasses of {@link HomepageCardController} are loaded here, which
|
||||
* will then trigger the {@link HomepageCardController} to load its data and listen to
|
||||
* corresponding changes. When every single {@link HomepageCardController} updates its data, the
|
||||
* data will be passed here, then going through some sorting mechanisms. The
|
||||
* {@link HomepageCardController} will end up building a list of {@link HomepageCard} for {@link
|
||||
* HomepageAdapter} and {@link BaseAdapter#notifyDataSetChanged()} will be called to get the page
|
||||
* refreshed.
|
||||
*/
|
||||
public class HomepageManager implements CardContentLoader.CardContentLoaderListener,
|
||||
HomepageCardUpdateListener {
|
||||
|
||||
private static final String TAG = "HomepageManager";
|
||||
//The list for Settings Custom Card
|
||||
@HomepageCard.CardType
|
||||
private static final int[] SETTINGS_CARDS = {HomepageCard.CardType.CONDITIONAL};
|
||||
|
||||
private final Context mContext;
|
||||
private final ControllerRendererPool mControllerRendererPool;
|
||||
private final Lifecycle mLifecycle;
|
||||
|
||||
private List<HomepageCard> mHomepageCards;
|
||||
private HomepageCardUpdateListener mListener;
|
||||
|
||||
|
||||
public HomepageManager(Context context, Lifecycle lifecycle) {
|
||||
mContext = context;
|
||||
mLifecycle = lifecycle;
|
||||
mHomepageCards = new ArrayList<>();
|
||||
mControllerRendererPool = new ControllerRendererPool();
|
||||
}
|
||||
|
||||
void startCardContentLoading() {
|
||||
final CardContentLoader cardContentLoader = new CardContentLoader();
|
||||
cardContentLoader.setListener(this);
|
||||
}
|
||||
|
||||
private void loadCardControllers() {
|
||||
if (mHomepageCards != null) {
|
||||
for (HomepageCard card : mHomepageCards) {
|
||||
setupController(card.getCardType());
|
||||
}
|
||||
}
|
||||
|
||||
//for data provided by Settings
|
||||
for (int cardType : SETTINGS_CARDS) {
|
||||
setupController(cardType);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupController(int cardType) {
|
||||
final HomepageCardController controller = mControllerRendererPool.getController(mContext,
|
||||
cardType);
|
||||
if (controller != null) {
|
||||
controller.setHomepageCardUpdateListener(this);
|
||||
controller.setLifecycle(mLifecycle);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO(b/111822376): implement sorting mechanism.
|
||||
private void sortCards() {
|
||||
//take mHomepageCards as the source and do the ranking based on the rule.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHomepageCardUpdated(int cardType, List<HomepageCard> updateList) {
|
||||
//TODO(b/112245748): Should implement a DiffCallback.
|
||||
//Keep the old list for comparison.
|
||||
final List<HomepageCard> prevCards = mHomepageCards;
|
||||
|
||||
//Remove the existing data that matches the certain cardType so as to insert the new data.
|
||||
for (int i = mHomepageCards.size() - 1; i >= 0; i--) {
|
||||
if (mHomepageCards.get(i).getCardType() == cardType) {
|
||||
mHomepageCards.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
//Append the new data
|
||||
mHomepageCards.addAll(updateList);
|
||||
|
||||
sortCards();
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onHomepageCardUpdated(HomepageCard.CardType.INVALID, mHomepageCards);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishCardLoading(List<HomepageCard> homepageCards) {
|
||||
mHomepageCards = homepageCards;
|
||||
|
||||
//Force card sorting here in case CardControllers of custom view have nothing to update
|
||||
// for the first launch.
|
||||
sortCards();
|
||||
|
||||
loadCardControllers();
|
||||
}
|
||||
|
||||
void setListener(HomepageCardUpdateListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public ControllerRendererPool getControllerRendererPool() {
|
||||
return mControllerRendererPool;
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,14 @@ public class BackgroundDataConditionController implements ConditionalCardControl
|
||||
static final int ID = Objects.hash("BackgroundDataConditionController");
|
||||
|
||||
private final Context mAppContext;
|
||||
private final ConditionManager mConditionManager;
|
||||
private final NetworkPolicyManager mNetworkPolicyManager;
|
||||
|
||||
public BackgroundDataConditionController(Context appContext) {
|
||||
public BackgroundDataConditionController(Context appContext, ConditionManager manager) {
|
||||
mAppContext = appContext;
|
||||
mConditionManager = manager;
|
||||
mNetworkPolicyManager =
|
||||
(NetworkPolicyManager) appContext.getSystemService(Context.NETWORK_POLICY_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,7 +45,7 @@ public class BackgroundDataConditionController implements ConditionalCardControl
|
||||
|
||||
@Override
|
||||
public boolean isDisplayable() {
|
||||
return NetworkPolicyManager.from(mAppContext).getRestrictBackground();
|
||||
return mNetworkPolicyManager.getRestrictBackground();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,7 +55,8 @@ public class BackgroundDataConditionController implements ConditionalCardControl
|
||||
|
||||
@Override
|
||||
public void onActionClick() {
|
||||
NetworkPolicyManager.from(mAppContext).setRestrictBackground(false);
|
||||
mNetworkPolicyManager.setRestrictBackground(false);
|
||||
mConditionManager.onConditionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -100,7 +100,6 @@ public class ConditionManager {
|
||||
*/
|
||||
public void onActionClick(long id) {
|
||||
getController(id).onActionClick();
|
||||
onConditionChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,15 +154,16 @@ public class ConditionManager {
|
||||
private void initCandidates() {
|
||||
// Initialize controllers first.
|
||||
mCardControllers.add(new AirplaneModeConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new BackgroundDataConditionController(mAppContext));
|
||||
mCardControllers.add(
|
||||
new BackgroundDataConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new BatterySaverConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new CellularDataConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new DndConditionCardController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new HotspotConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new NightDisplayConditionController(mAppContext));
|
||||
mCardControllers.add(new NightDisplayConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new RingerVibrateConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new RingerMutedConditionController(mAppContext, this /* manager */));
|
||||
mCardControllers.add(new WorkModeConditionController(mAppContext));
|
||||
mCardControllers.add(new WorkModeConditionController(mAppContext, this /* manager */));
|
||||
|
||||
// Initialize ui model later. UI model depends on controller.
|
||||
mCandidates.add(new AirplaneModeConditionCard(mAppContext));
|
||||
|
||||
@@ -30,10 +30,12 @@ public class NightDisplayConditionController implements ConditionalCardControlle
|
||||
ColorDisplayController.Callback {
|
||||
static final int ID = Objects.hash("NightDisplayConditionController");
|
||||
|
||||
private final ConditionManager mConditionManager;
|
||||
private final ColorDisplayController mController;
|
||||
|
||||
public NightDisplayConditionController(Context appContext) {
|
||||
public NightDisplayConditionController(Context appContext, ConditionManager manager) {
|
||||
mController = new ColorDisplayController(appContext);
|
||||
mConditionManager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,4 +71,9 @@ public class NightDisplayConditionController implements ConditionalCardControlle
|
||||
public void stopMonitoringStateChange() {
|
||||
mController.setListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivated(boolean activated) {
|
||||
mConditionManager.onConditionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package com.android.settings.homepage.conditional;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.Settings;
|
||||
|
||||
@@ -31,13 +34,25 @@ public class WorkModeConditionController implements ConditionalCardController {
|
||||
|
||||
static final int ID = Objects.hash("WorkModeConditionController");
|
||||
|
||||
private static final IntentFilter FILTER = new IntentFilter();
|
||||
|
||||
static {
|
||||
FILTER.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
|
||||
FILTER.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
private final Context mAppContext;
|
||||
private final UserManager mUm;
|
||||
private final ConditionManager mConditionManager;
|
||||
private final Receiver mReceiver;
|
||||
|
||||
private UserHandle mUserHandle;
|
||||
|
||||
public WorkModeConditionController(Context appContext) {
|
||||
public WorkModeConditionController(Context appContext, ConditionManager manager) {
|
||||
mAppContext = appContext;
|
||||
mUm = mAppContext.getSystemService(UserManager.class);
|
||||
mConditionManager = manager;
|
||||
mReceiver = new Receiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,12 +81,12 @@ public class WorkModeConditionController implements ConditionalCardController {
|
||||
|
||||
@Override
|
||||
public void startMonitoringStateChange() {
|
||||
|
||||
mAppContext.registerReceiver(mReceiver, FILTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopMonitoringStateChange() {
|
||||
|
||||
mAppContext.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
private void updateUserHandle() {
|
||||
@@ -87,4 +102,15 @@ public class WorkModeConditionController implements ConditionalCardController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Receiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
|
||||
|| TextUtils.equals(action, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
|
||||
mConditionManager.onConditionChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static android.os.UserHandle.myUserId;
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
@@ -27,7 +28,9 @@ import android.provider.Settings;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.ServiceState;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
@@ -44,7 +47,12 @@ import androidx.preference.PreferenceScreen;
|
||||
public class MobileNetworkPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
|
||||
|
||||
private static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings";
|
||||
@VisibleForTesting
|
||||
static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings";
|
||||
@VisibleForTesting
|
||||
static final String MOBILE_NETWORK_PACKAGE = "com.android.phone";
|
||||
@VisibleForTesting
|
||||
static final String MOBILE_NETWORK_CLASS = "com.android.phone.MobileNetworkSettings";
|
||||
|
||||
private final boolean mIsSecondaryUser;
|
||||
private final TelephonyManager mTelephonyManager;
|
||||
@@ -134,6 +142,22 @@ public class MobileNetworkPreferenceController extends AbstractPreferenceControl
|
||||
mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (KEY_MOBILE_NETWORK_SETTINGS.equals(preference.getKey())) {
|
||||
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.MOBILE_NETWORK_V2)) {
|
||||
//TODO(b/110260193): go to the mobile network page existed in settings
|
||||
} else {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(
|
||||
new ComponentName(MOBILE_NETWORK_PACKAGE, MOBILE_NETWORK_CLASS));
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mTelephonyManager.getNetworkOperatorName();
|
||||
|
||||
@@ -93,7 +93,8 @@ public class ValidatedEditTextPreference extends CustomEditTextPreferenceCompat
|
||||
textView.setInputType(
|
||||
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
} else {
|
||||
textView.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
textView.setInputType(
|
||||
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.applications;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.settings.notification.EmergencyBroadcastPreferenceController;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class AppAndNotificationDashboardFragmentTest {
|
||||
|
||||
@Test
|
||||
@Config(shadows = {ShadowEmergencyBroadcastPreferenceController.class})
|
||||
public void getNonIndexableKeys_shouldIncludeSpecialAppAccess() {
|
||||
final Context context = spy(RuntimeEnvironment.application);
|
||||
UserManager manager = mock(UserManager.class);
|
||||
when(manager.isAdminUser()).thenReturn(true);
|
||||
when(context.getSystemService(Context.USER_SERVICE)).thenReturn(manager);
|
||||
final List<String> niks = AppAndNotificationDashboardFragment.SEARCH_INDEX_DATA_PROVIDER
|
||||
.getNonIndexableKeys(context);
|
||||
|
||||
assertThat(niks).contains(
|
||||
new SpecialAppAccessPreferenceController(context).getPreferenceKey());
|
||||
}
|
||||
|
||||
@Implements(EmergencyBroadcastPreferenceController.class)
|
||||
public static class ShadowEmergencyBroadcastPreferenceController {
|
||||
|
||||
@Implementation
|
||||
public boolean isAvailable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,25 @@
|
||||
|
||||
package com.android.settings.applications;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.datausage.DataSaverBackend;
|
||||
import com.android.settings.datausage.AppStateDataUsageBridge;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowApplicationsState;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -32,48 +42,56 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowUserManager.class, ShadowApplicationsState.class})
|
||||
public class SpecialAppAccessPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private DataSaverBackend mBackend;
|
||||
private ApplicationsState.Session mSession;
|
||||
@Mock
|
||||
private Preference mPreference;
|
||||
private PreferenceScreen mScreen;
|
||||
|
||||
private SpecialAppAccessPreferenceController mController;
|
||||
private Preference mPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new SpecialAppAccessPreferenceController(mContext);
|
||||
ReflectionHelpers.setField(mController, "mDataSaverBackend", mBackend);
|
||||
ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{0});
|
||||
mController = new SpecialAppAccessPreferenceController(mContext, "test_key");
|
||||
mPreference = new Preference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
|
||||
mController.mSession = mSession;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_shouldAlwaysReturnTrue() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
public void getAvailabilityState_unsearchable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_shouldSetSummary() {
|
||||
when(mBackend.getWhitelistedCount()).thenReturn(0);
|
||||
final ArrayList<ApplicationsState.AppEntry> apps = new ArrayList<>();
|
||||
final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
|
||||
entry.hasLauncherEntry = true;
|
||||
entry.info = new ApplicationInfo();
|
||||
entry.extraInfo = new AppStateDataUsageBridge.DataUsageState(
|
||||
true /* whitelisted */, false /* blacklisted */);
|
||||
apps.add(entry);
|
||||
when(mSession.getAllApps()).thenReturn(apps);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
mController.displayPreference(mScreen);
|
||||
mController.onExtraInfoUpdated();
|
||||
|
||||
verify(mPreference).setSummary(mContext.getResources()
|
||||
.getQuantityString(R.plurals.special_access_summary, 0, 0));
|
||||
|
||||
when(mBackend.getWhitelistedCount()).thenReturn(1);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setSummary(mContext.getResources()
|
||||
.getQuantityString(R.plurals.special_access_summary, 1, 1));
|
||||
assertThat(mPreference.getSummary())
|
||||
.isEqualTo(mContext.getResources().getQuantityString(
|
||||
R.plurals.special_access_summary, 1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package com.android.settings.applications.defaultapps;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyList;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -29,6 +29,8 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.UserManager;
|
||||
@@ -45,14 +47,14 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class DefaultHomePreferenceControllerTest {
|
||||
|
||||
private static final String TEST_PACKAGE = "test.pkg";
|
||||
private static final String TEST_CLASS = "class";
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
@@ -107,14 +109,14 @@ public class DefaultHomePreferenceControllerTest {
|
||||
@Test
|
||||
public void testIsHomeDefault_noDefaultSet_shouldReturnTrue() {
|
||||
when(mPackageManager.getHomeActivities(anyList())).thenReturn(null);
|
||||
assertThat(DefaultHomePreferenceController.isHomeDefault("test.pkg", mPackageManager))
|
||||
assertThat(DefaultHomePreferenceController.isHomeDefault(TEST_PACKAGE, mPackageManager))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsHomeDefault_defaultSetToPkg_shouldReturnTrue() {
|
||||
final String pkgName = "test.pkg";
|
||||
final ComponentName defaultHome = new ComponentName(pkgName, "class");
|
||||
final String pkgName = TEST_PACKAGE;
|
||||
final ComponentName defaultHome = new ComponentName(pkgName, TEST_CLASS);
|
||||
|
||||
when(mPackageManager.getHomeActivities(anyList())).thenReturn(defaultHome);
|
||||
|
||||
@@ -124,8 +126,8 @@ public class DefaultHomePreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void testIsHomeDefault_defaultSetToOtherPkg_shouldReturnFalse() {
|
||||
final String pkgName = "test.pkg";
|
||||
final ComponentName defaultHome = new ComponentName("not" + pkgName, "class");
|
||||
final String pkgName = TEST_PACKAGE;
|
||||
final ComponentName defaultHome = new ComponentName("not" + pkgName, TEST_CLASS);
|
||||
|
||||
when(mPackageManager.getHomeActivities(anyList())).thenReturn(defaultHome);
|
||||
|
||||
@@ -136,29 +138,28 @@ public class DefaultHomePreferenceControllerTest {
|
||||
@Test
|
||||
public void testGetSettingIntent_homeHasNoSetting_shouldNotReturnSettingIntent() {
|
||||
when(mPackageManager.getHomeActivities(anyList()))
|
||||
.thenReturn(new ComponentName("test.pkg", "class"));
|
||||
.thenReturn(new ComponentName(TEST_PACKAGE, TEST_CLASS));
|
||||
when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
|
||||
.thenReturn(null);
|
||||
|
||||
assertThat(mController.getSettingIntent(mController.getDefaultAppInfo())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSettingIntent_homeHasOneSetting_shouldReturnSettingIntent() {
|
||||
when(mPackageManager.getHomeActivities(anyList()))
|
||||
.thenReturn(new ComponentName("test.pkg", "class"));
|
||||
when(mPackageManager.queryIntentActivities(any(), eq(0)))
|
||||
.thenReturn(Collections.singletonList(mock(ResolveInfo.class)));
|
||||
.thenReturn(new ComponentName(TEST_PACKAGE, TEST_CLASS));
|
||||
final ResolveInfo info = mock(ResolveInfo.class);
|
||||
info.activityInfo = mock(ActivityInfo.class);
|
||||
info.activityInfo.name = TEST_CLASS;
|
||||
info.activityInfo.applicationInfo = mock(ApplicationInfo.class);
|
||||
info.activityInfo.applicationInfo.packageName = TEST_PACKAGE;
|
||||
when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
|
||||
.thenReturn(info);
|
||||
|
||||
Intent intent = mController.getSettingIntent(mController.getDefaultAppInfo());
|
||||
assertThat(intent).isNotNull();
|
||||
assertThat(intent.getPackage()).isEqualTo("test.pkg");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSettingIntent_homeHasMultipleSettings_shouldNotReturnSettingIntent() {
|
||||
when(mPackageManager.getHomeActivities(anyList()))
|
||||
.thenReturn(new ComponentName("test.pkg", "class"));
|
||||
when(mPackageManager.queryIntentActivities(any(), eq(0)))
|
||||
.thenReturn(Arrays.asList(mock(ResolveInfo.class), mock(ResolveInfo.class)));
|
||||
assertThat(mController.getSettingIntent(mController.getDefaultAppInfo())).isNull();
|
||||
assertThat(intent.getPackage()).isEqualTo(TEST_PACKAGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -31,8 +31,8 @@ import android.content.Context;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowBluetoothDevice;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
@@ -55,7 +55,7 @@ import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(shadows = SettingsShadowBluetoothDevice.class)
|
||||
@Config(shadows = {SettingsShadowBluetoothDevice.class, ShadowBluetoothDevice.class})
|
||||
public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
private BluetoothDetailsProfilesController mController;
|
||||
@@ -290,8 +290,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
@Test
|
||||
public void pbapProfileStartsEnabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
when(mCachedDevice.getPhonebookPermissionChoice())
|
||||
.thenReturn(CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_pbap);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
@@ -306,14 +305,14 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||
verify(mCachedDevice).setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
|
||||
assertThat(mDevice.getPhonebookAccessPermission())
|
||||
.isEqualTo(BluetoothDevice.ACCESS_REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pbapProfileStartsDisabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
when(mCachedDevice.getPhonebookPermissionChoice())
|
||||
.thenReturn(CachedBluetoothDevice.ACCESS_REJECTED);
|
||||
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_pbap);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
@@ -328,7 +327,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||
verify(mCachedDevice).setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
|
||||
assertThat(mDevice.getPhonebookAccessPermission())
|
||||
.isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -338,8 +338,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
when(mapProfile.getNameResource(mDevice)).thenReturn(R.string.bluetooth_profile_map);
|
||||
when(mProfileManager.getMapProfile()).thenReturn(mapProfile);
|
||||
when(mProfileManager.getProfileByName(eq(mapProfile.toString()))).thenReturn(mapProfile);
|
||||
when(mCachedDevice.getMessagePermissionChoice())
|
||||
.thenReturn(CachedBluetoothDevice.ACCESS_REJECTED);
|
||||
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
showScreen(mController);
|
||||
List<SwitchPreference> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
@@ -349,7 +348,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||
verify(mCachedDevice).setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
|
||||
assertThat(mDevice.getMessageAccessPermission()).isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
private A2dpProfile addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio,
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowParcel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class HighFrequencyPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private IBinder mSurfaceFlingerBinder;
|
||||
|
||||
private HighFrequencyDisplayPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mController = spy(new HighFrequencyDisplayPreferenceController(mContext));
|
||||
ReflectionHelpers.setField(mController, "mSurfaceFlingerBinder", mSurfaceFlingerBinder);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
mController.displayPreference(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingToggledOn_shouldWriteTrueToHighFrequencySetting() {
|
||||
mController.onPreferenceChange(mPreference, true /* new value */);
|
||||
|
||||
verify(mController).writeHighFrequencyDisplaySetting(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingToggledOff_shouldWriteFalseToHighFrequencySetting() {
|
||||
mController.onPreferenceChange(mPreference, false /* new value */);
|
||||
|
||||
verify(mController).writeHighFrequencyDisplaySetting(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingEnabled_shouldCheckPreference() throws RemoteException {
|
||||
mController.writeHighFrequencyDisplaySetting(true);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mController).readHighFrequencyDisplaySetting();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingDisabled_shouldUnCheckPreference() throws RemoteException {
|
||||
mController.writeHighFrequencyDisplaySetting(true);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mController).readHighFrequencyDisplaySetting();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_preferenceChecked_shouldTurnOffPreference() {
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
verify(mController).writeHighFrequencyDisplaySetting(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_preferenceUnchecked_shouldNotTurnOffPreference() {
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
verify(mController).writeHighFrequencyDisplaySetting(false);
|
||||
}
|
||||
}
|
||||
@@ -22,28 +22,37 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.NetworkPolicyManager;
|
||||
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.homepage.conditional.BackgroundDataConditionController;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class BackgroundDataConditionControllerTest {
|
||||
|
||||
@Mock
|
||||
private ConditionManager mConditionManager;
|
||||
@Mock
|
||||
private NetworkPolicyManager mNetworkPolicyManager;
|
||||
private Context mContext;
|
||||
private BackgroundDataConditionController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
ShadowApplication.getInstance().setSystemService(Context.NETWORK_POLICY_SERVICE,
|
||||
mNetworkPolicyManager);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mController = new BackgroundDataConditionController(mContext);
|
||||
mController = new BackgroundDataConditionController(mContext, mConditionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -56,4 +65,10 @@ public class BackgroundDataConditionControllerTest {
|
||||
assertThat(intent.getComponent().getClassName()).isEqualTo(
|
||||
Settings.DataUsageSummaryActivity.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActionClick_shouldRefreshCondition() {
|
||||
mController.onActionClick();
|
||||
verify(mConditionManager).onConditionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.homepage.conditional;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class NightDisplayConditionControllerTest {
|
||||
|
||||
@Mock
|
||||
private ConditionManager mConditionManager;
|
||||
|
||||
private Context mContext;
|
||||
private NightDisplayConditionController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new NightDisplayConditionController(mContext, mConditionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivated_shouldUpdateCondition() {
|
||||
mController.onActivated(true);
|
||||
|
||||
verify(mConditionManager).onConditionChanged();
|
||||
}
|
||||
}
|
||||
@@ -24,24 +24,28 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.homepage.conditional.WorkModeConditionController;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
public class WorkModeConditionControllerTest {
|
||||
|
||||
@Mock
|
||||
private ConditionManager mConditionManager;
|
||||
private Context mContext;
|
||||
private WorkModeConditionController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mController = new WorkModeConditionController(mContext);
|
||||
mController = new WorkModeConditionController(mContext, mConditionManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
package com.android.settings.network;
|
||||
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_START;
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
|
||||
import static com.android.settings.network.MobileNetworkPreferenceController.MOBILE_NETWORK_CLASS;
|
||||
import static com.android.settings.network.MobileNetworkPreferenceController.MOBILE_NETWORK_PACKAGE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -25,14 +27,21 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.shadow.api.Shadow.extract;
|
||||
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_START;
|
||||
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Global;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
@@ -43,6 +52,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
@@ -68,6 +78,7 @@ public class MobileNetworkPreferenceControllerTest {
|
||||
private Lifecycle mLifecycle;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private MobileNetworkPreferenceController mController;
|
||||
private Preference mPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -76,6 +87,8 @@ public class MobileNetworkPreferenceControllerTest {
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
|
||||
mPreference = new Preference(mContext);
|
||||
mPreference.setKey(MobileNetworkPreferenceController.KEY_MOBILE_NETWORK_SETTINGS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -173,4 +186,18 @@ public class MobileNetworkPreferenceControllerTest {
|
||||
mController.updateState(mPreference);
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_mobileFeatureDisabled_sendIntent() {
|
||||
mController = new MobileNetworkPreferenceController(mContext);
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.MOBILE_NETWORK_V2, false);
|
||||
ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mContext).startActivity(argument.capture());
|
||||
final ComponentName componentName = argument.getValue().getComponent();
|
||||
assertThat(componentName.getPackageName()).isEqualTo(MOBILE_NETWORK_PACKAGE);
|
||||
assertThat(componentName.getClassName()).isEqualTo(MOBILE_NETWORK_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
@Implements(ApplicationsState.class)
|
||||
public class ShadowApplicationsState {
|
||||
@Implementation
|
||||
public Looper getBackgroundLooper() {
|
||||
return Looper.getMainLooper();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.testutils.shadow;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
@Implements(value = BluetoothDevice.class, inheritImplementationMethods = true)
|
||||
public class ShadowBluetoothDevice extends org.robolectric.shadows.ShadowBluetoothDevice {
|
||||
|
||||
private int mMessageAccessPermission = BluetoothDevice.ACCESS_UNKNOWN;
|
||||
private int mPhonebookAccessPermission = BluetoothDevice.ACCESS_UNKNOWN;
|
||||
private int mSimAccessPermission = BluetoothDevice.ACCESS_UNKNOWN;
|
||||
|
||||
@Implementation
|
||||
public void setMessageAccessPermission(int value) {
|
||||
mMessageAccessPermission = value;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getMessageAccessPermission() {
|
||||
return mMessageAccessPermission;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public void setPhonebookAccessPermission(int value) {
|
||||
mPhonebookAccessPermission = value;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getPhonebookAccessPermission() {
|
||||
return mPhonebookAccessPermission;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public void setSimAccessPermission(int value) {
|
||||
mSimAccessPermission = value;
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public int getSimAccessPermission() {
|
||||
return mSimAccessPermission;
|
||||
}
|
||||
}
|
||||
@@ -131,4 +131,16 @@ public class ValidatedEditTextPreferenceTest {
|
||||
& (InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_CLASS_TEXT))
|
||||
.isNotEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindViewHolder_isNotPassword_shouldNotAutoCorrectText() {
|
||||
final TextView textView = spy(new TextView(RuntimeEnvironment.application));
|
||||
when(mViewHolder.findViewById(android.R.id.summary)).thenReturn(textView);
|
||||
|
||||
mPreference.setIsSummaryPassword(false);
|
||||
mPreference.onBindViewHolder(mViewHolder);
|
||||
|
||||
assertThat(textView.getInputType()).isEqualTo(
|
||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user