Snap for 4977457 from a60d914d5f to qt-release

Change-Id: Id50a0d62d65f396538098278f4eabea74a267201
This commit is contained in:
android-build-team Robot
2018-08-26 03:04:41 +00:00
46 changed files with 1703 additions and 227 deletions

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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() {
}
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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";
}

View File

@@ -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);
}
}

View File

@@ -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));

View File

@@ -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
}
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;

View 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;
}
}
}

View File

@@ -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;
}
}

View 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();
}
}

View 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);
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View 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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View 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;
}
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}