From 27475bacd74674bcd150fda7a8486d61ede67c07 Mon Sep 17 00:00:00 2001 From: Yiling Chuang Date: Mon, 8 Jul 2024 03:09:50 +0000 Subject: [PATCH 001/350] FRP bypass defense in App battery usage page Before the setup flow completion, don't allow the app info page in App battery usage to be launched. Bug: 327748846 Test: atest SettingsRoboTests + manual test - factory reset + launch app battery usage app info via ADB during Setup -> verify app closes Flag : EXEMPT bugfix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:419a6a907902a12a0f565c808fa70092004d6686) Merged-In: I486820ca2afecc02729a56a3c531fb931c1907d0 Change-Id: I486820ca2afecc02729a56a3c531fb931c1907d0 --- .../android/settings/fuelgauge/AdvancedPowerUsageDetail.java | 5 +++++ .../settings/fuelgauge/AdvancedPowerUsageDetailTest.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 005c0730343..7bbb06a2d30 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -268,6 +268,11 @@ public void onResume() { mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); } + @Override + protected boolean shouldSkipForInitialSUW() { + return true; + } + @Override public void onPause() { super.onPause(); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 80739e9d47a..1bc00a1fdff 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -441,4 +441,9 @@ public void onPause_optimizationModeIsNotChanged_notInvokeLogging() TimeUnit.SECONDS.sleep(1); verifyNoInteractions(mMetricsFeatureProvider); } + + @Test + public void shouldSkipForInitialSUW_returnTrue() { + assertThat(mFragment.shouldSkipForInitialSUW()).isTrue(); + } } From 7cf67cd32e7d057f0431f684b8f0312c871d0929 Mon Sep 17 00:00:00 2001 From: Rambo Wang Date: Fri, 23 Aug 2024 14:43:07 +0000 Subject: [PATCH 002/350] Turn off RILD reset in Reset Network Settings Flow The CL temporarily removes RILD reset in the flow and plans to bring it back when a more reliable solution is available. Resetting RID in the flow may cause device failure to camp on the default cellular network. The interface used to reset RILD in the flow was designed for telephony process silent restart feature. The implementation doesn't work well together with other reset options (e.g. nvResetConfig). Bug: 356272264 Test: Execute feature test plan, including regression test Flag: com.android.internal.telephony.flags.reset_mobile_network_settings (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:dd7d18ec8ac12156de11d20b1179216680cd4073) Merged-In: I955bcb0b151f27dc73c03a580c0144bea9e3bfa3 Change-Id: I955bcb0b151f27dc73c03a580c0144bea9e3bfa3 --- src/com/android/settings/ResetNetwork.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java index c91ef5cdb93..2619e7d6c99 100644 --- a/src/com/android/settings/ResetNetwork.java +++ b/src/com/android/settings/ResetNetwork.java @@ -131,7 +131,6 @@ void showFinalConfirmation() { if (Flags.resetMobileNetworkSettings()) { resetOptions |= ResetNetworkRequest.RESET_IMS_STACK; resetOptions |= ResetNetworkRequest.RESET_PHONE_PROCESS; - resetOptions |= ResetNetworkRequest.RESET_RILD; } ResetNetworkRequest request = new ResetNetworkRequest(resetOptions); if (mSubscriptions != null && mSubscriptions.size() > 0) { From 8c8d3eac3bdade4e7e15c95c401e2bb6fbb3e8b8 Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Mon, 22 Jul 2024 17:03:12 -0700 Subject: [PATCH 003/350] startActivityForResult with new Intent Rather than use the raw Intent, we make a copy of it. See bug. Bug: 330722900 Flag: EXEMPT bugfix Test: manual Test: atest com.android.settings.users.UserSettingsTest com.android.settings.users.UserDetailsSettingsTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1189e24e47571eae86634aeaa7dc60b8fe7f4820) Merged-In: Id74e4b7ae261f2916eedaef04a679f83409a4b67 Change-Id: Id74e4b7ae261f2916eedaef04a679f83409a4b67 --- src/com/android/settings/users/AppRestrictionsFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index 1532448718c..c42e2f57b1d 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -651,7 +651,7 @@ public void onReceive(Context context, Intent intent) { int requestCode = generateCustomActivityRequestCode( RestrictionsResultReceiver.this.preference); AppRestrictionsFragment.this.startActivityForResult( - restrictionsIntent, requestCode); + new Intent(restrictionsIntent), requestCode); } } } From b86100f35411e218522f9c528742d6d7fe3c325d Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Mon, 22 Jul 2024 16:12:46 +0800 Subject: [PATCH 004/350] Checks cross user permission before handling intent Bug: 326057017 Test: atest Flag: EXEMPT bug fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d3b3edd45167515579ab156533754e56ac813f35) Merged-In: I3444e55b22b7487f96b0e3e9deb3f844c4c4723a Change-Id: I3444e55b22b7487f96b0e3e9deb3f844c4c4723a --- .../settings/applications/AppInfoBase.java | 29 ++++++++++++++ .../applications/AppInfoWithHeaderTest.java | 38 +++++++++++++++++++ .../appcompat/UserAspectRatioDetailsTest.java | 31 ++++++++++----- 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index 2c41be4ad41..c056add7ef3 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -18,6 +18,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.app.admin.DevicePolicyManager; @@ -39,6 +40,7 @@ import android.text.TextUtils; import android.util.Log; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -134,6 +136,11 @@ protected String retrieveAppEntry() { mPackageName = intent.getData().getSchemeSpecificPart(); } } + if (!hasInteractAcrossUsersPermission()) { + Log.w(TAG, "Intent not valid."); + finish(); + return ""; + } if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { mUserId = ((UserHandle) intent.getParcelableExtra( Intent.EXTRA_USER_HANDLE)).getIdentifier(); @@ -163,6 +170,28 @@ protected String retrieveAppEntry() { return mPackageName; } + @VisibleForTesting + protected boolean hasInteractAcrossUsersPermission() { + Activity activity = getActivity(); + if (!(activity instanceof SettingsActivity)) { + return false; + } + final String callingPackageName = + ((SettingsActivity) activity).getInitialCallingPackage(); + + if (TextUtils.isEmpty(callingPackageName)) { + Log.w(TAG, "Not able to get calling package name for permission check"); + return false; + } + if (mPm.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPackageName) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "Package " + callingPackageName + " does not have required permission " + + Manifest.permission.INTERACT_ACROSS_USERS_FULL); + return false; + } + return true; + } + protected void setIntentAndFinish(boolean appChanged) { Log.i(TAG, "appChanged=" + appChanged); Intent intent = new Intent(); diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java index 562212e3569..cb121ea3c05 100644 --- a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java @@ -171,6 +171,32 @@ public void extraUserHandleInIntent_retrieveAppEntryWithMyUserId() assertThat(mAppInfoWithHeader.mAppEntry).isNotNull(); } + @Test + public void noCrossUserPermission_retrieveAppEntry_fail() + throws PackageManager.NameNotFoundException { + TestFragmentWithoutPermission testFragmentWithoutPermission = + new TestFragmentWithoutPermission(); + final int userId = 1002; + final String packageName = "com.android.settings"; + + testFragmentWithoutPermission.mIntent.putExtra(Intent.EXTRA_USER_HANDLE, + new UserHandle(userId)); + testFragmentWithoutPermission.mIntent.setData(Uri.fromParts("package", + packageName, null)); + final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class); + entry.info = new ApplicationInfo(); + entry.info.packageName = packageName; + + when(testFragmentWithoutPermission.mState.getEntry(packageName, userId)).thenReturn(entry); + when(testFragmentWithoutPermission.mPm.getPackageInfoAsUser(eq(entry.info.packageName), + any(), eq(userId))).thenReturn( + testFragmentWithoutPermission.mPackageInfo); + + testFragmentWithoutPermission.retrieveAppEntry(); + + assertThat(testFragmentWithoutPermission.mAppEntry).isNull(); + } + public static class TestFragment extends AppInfoWithHeader { PreferenceManager mManager; @@ -223,6 +249,11 @@ public Context getContext() { return mShadowContext; } + @Override + protected boolean hasInteractAcrossUsersPermission() { + return true; + } + @Override protected void onPackageRemoved() { mPackageRemovedCalled = true; @@ -233,4 +264,11 @@ protected Intent getIntent() { return mIntent; } } + + private static final class TestFragmentWithoutPermission extends TestFragment { + @Override + protected boolean hasInteractAcrossUsersPermission() { + return false; + } + } } diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java index ce03a6def34..d597b7ea045 100644 --- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java @@ -39,23 +39,26 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; +import android.app.Application; import android.app.IActivityManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.RemoteException; +import android.os.UserHandle; -import androidx.fragment.app.testing.EmptyFragmentActivity; import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.rules.ActivityScenarioRule; +import com.android.settings.SettingsActivity; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -71,14 +74,14 @@ @Config(shadows = {ShadowActivityManager.class, ShadowFragment.class}) public class UserAspectRatioDetailsTest { - @Rule - public ActivityScenarioRule rule = - new ActivityScenarioRule<>(EmptyFragmentActivity.class); - @Mock private UserAspectRatioManager mUserAspectRatioManager; @Mock private IActivityManager mAm; + @Mock + private PackageManager mPackageManager; + @Mock + private SettingsActivity mSettingsActivity; private RadioWithImagePreference mRadioButtonPref; private Context mContext; @@ -93,6 +96,12 @@ public void setUp() { mFragment = spy(new UserAspectRatioDetails()); when(mFragment.getContext()).thenReturn(mContext); when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager); + when(mFragment.getActivity()).thenReturn(mSettingsActivity); + when(mSettingsActivity.getApplication()).thenReturn((Application) mContext); + when(mSettingsActivity.getInitialCallingPackage()).thenReturn("test.package"); + when(mSettingsActivity.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), + any())).thenReturn(PackageManager.PERMISSION_GRANTED); when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt())) .thenReturn(false); ShadowActivityManager.setService(mAm); @@ -111,8 +120,10 @@ public void testOrderOfOptionsFollowsConfig() { .getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_FULLSCREEN); doReturn(2).when(mUserAspectRatioManager) .getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_UNSET); - rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity()); final Bundle args = new Bundle(); + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0)); + args.putParcelable("intent", intent); args.putString(ARG_PACKAGE_NAME, anyString()); mFragment.setArguments(args); mFragment.onCreate(Bundle.EMPTY); @@ -196,8 +207,10 @@ public void onButtonClicked_overrideEnabled_fullscreenPreselected() doReturn(true).when(mUserAspectRatioManager) .hasAspectRatioOption(anyInt(), anyString()); - rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity()); final Bundle args = new Bundle(); + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0)); + args.putParcelable("intent", intent); args.putString(ARG_PACKAGE_NAME, anyString()); mFragment.setArguments(args); mFragment.onCreate(Bundle.EMPTY); From dcf8f310eef5bf158682def144a068b13f9c9900 Mon Sep 17 00:00:00 2001 From: Chris Antol Date: Thu, 29 Aug 2024 19:50:55 +0000 Subject: [PATCH 005/350] Only check INTERACT_ACROSS_USERS_FULL when user handle is not current Bug: 326057017 Test: atest; open app info pages from both primary and secondary profile -> verify they open as expected Flag: EXEMPT bug fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:78ee160c20eeb41bb7eccfde9af772a9b1a958d6) Merged-In: I83ab4461f28e7f40c676099213c840a1a7dc932a Change-Id: I83ab4461f28e7f40c676099213c840a1a7dc932a --- .../settings/applications/AppInfoBase.java | 16 ++++++++-------- .../applications/AppInfoWithHeaderTest.java | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index c056add7ef3..1d774826c2d 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -136,14 +136,14 @@ protected String retrieveAppEntry() { mPackageName = intent.getData().getSchemeSpecificPart(); } } - if (!hasInteractAcrossUsersPermission()) { - Log.w(TAG, "Intent not valid."); - finish(); - return ""; - } if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { - mUserId = ((UserHandle) intent.getParcelableExtra( - Intent.EXTRA_USER_HANDLE)).getIdentifier(); + mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE)) + .getIdentifier(); + if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) { + Log.w(TAG, "Intent not valid."); + finish(); + return ""; + } } else { mUserId = UserHandle.myUserId(); } @@ -171,7 +171,7 @@ protected String retrieveAppEntry() { } @VisibleForTesting - protected boolean hasInteractAcrossUsersPermission() { + protected boolean hasInteractAcrossUsersFullPermission() { Activity activity = getActivity(); if (!(activity instanceof SettingsActivity)) { return false; diff --git a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java index cb121ea3c05..0ed56c0fec6 100644 --- a/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java +++ b/tests/robotests/src/com/android/settings/applications/AppInfoWithHeaderTest.java @@ -250,7 +250,7 @@ public Context getContext() { } @Override - protected boolean hasInteractAcrossUsersPermission() { + protected boolean hasInteractAcrossUsersFullPermission() { return true; } @@ -267,7 +267,7 @@ protected Intent getIntent() { private static final class TestFragmentWithoutPermission extends TestFragment { @Override - protected boolean hasInteractAcrossUsersPermission() { + protected boolean hasInteractAcrossUsersFullPermission() { return false; } } From 7c2681d92ad3bae837df51751331609073815e06 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Wed, 14 Aug 2024 21:02:27 +0000 Subject: [PATCH 006/350] Stops hiding a11y services with the same package+label as an activity. Bug: 353700779 Test: atest AccessibilitySettings, ensure new test passes Test: revert AccessibilitySettings.java change, ensure new test fails Test: Install poc APKs from the bug, observe issue not reproducible Flag: NONE security fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c38fd822ba27224c179b80de2b89a1e9ab8bee59) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ccc0d9d0ebace1cf0925b7c696bc41d6cf9efe52) Merged-In: Ie680e80169aa734f2559fe50ef06e4d1eae46779 Change-Id: Ie680e80169aa734f2559fe50ef06e4d1eae46779 --- .../accessibility/AccessibilitySettings.java | 27 +------- .../AccessibilitySettingsTest.java | 64 ++++++++++++++----- .../shadow/ShadowAccessibilityManager.java | 24 ++++++- 3 files changed, 71 insertions(+), 44 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 8441c2acaa5..ecfda27237f 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -21,7 +21,6 @@ import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Context; -import android.content.pm.ServiceInfo; import android.hardware.input.InputManager; import android.os.Bundle; import android.os.Handler; @@ -29,7 +28,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.Pair; import android.view.InputDevice; import android.view.accessibility.AccessibilityManager; @@ -57,8 +55,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; /** Activity with the accessibility settings. */ @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @@ -458,20 +454,9 @@ private List getInstalledAccessibilityList(Context context UserHandle.myUserId()); final List activityList = preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList); - final Set> packageLabelPairs = - activityList.stream() - .map(a11yActivityPref -> new Pair<>( - a11yActivityPref.getPackageName(), a11yActivityPref.getLabel()) - ).collect(Collectors.toSet()); - - // Remove duplicate item here, new a ArrayList to copy unmodifiable list result - // (getInstalledAccessibilityServiceList). + final List installedServiceList = new ArrayList<>( a11yManager.getInstalledAccessibilityServiceList()); - if (!packageLabelPairs.isEmpty()) { - installedServiceList.removeIf( - target -> containsPackageAndLabelInList(packageLabelPairs, target)); - } final List serviceList = preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList); @@ -482,16 +467,6 @@ private List getInstalledAccessibilityList(Context context return preferenceList; } - private boolean containsPackageAndLabelInList( - Set> packageLabelPairs, - AccessibilityServiceInfo targetServiceInfo) { - final ServiceInfo serviceInfo = targetServiceInfo.getResolveInfo().serviceInfo; - final String servicePackageName = serviceInfo.packageName; - final CharSequence serviceLabel = serviceInfo.loadLabel(getPackageManager()); - - return packageLabelPairs.contains(new Pair<>(servicePackageName, serviceLabel)); - } - private void initializePreBundledServicesMapFromArray(String categoryKey, int key) { String[] services = getResources().getStringArray(key); PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 1463cd0b7f9..41206068d18 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -32,6 +32,7 @@ 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.content.pm.ServiceInfo; import android.database.ContentObserver; @@ -50,6 +51,7 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.testutils.XmlTestUtils; +import com.android.settings.testutils.shadow.ShadowAccessibilityManager; import com.android.settings.testutils.shadow.ShadowApplicationPackageManager; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothUtils; @@ -75,7 +77,6 @@ import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -import org.robolectric.shadows.ShadowAccessibilityManager; import org.robolectric.shadows.ShadowContentResolver; import org.xmlpull.v1.XmlPullParserException; @@ -87,6 +88,7 @@ /** Test for {@link AccessibilitySettings}. */ @RunWith(RobolectricTestRunner.class) @Config(shadows = { + ShadowAccessibilityManager.class, ShadowBluetoothAdapter.class, ShadowUserManager.class, ShadowColorDisplayManager.class, @@ -95,8 +97,10 @@ }) public class AccessibilitySettingsTest { private static final String PACKAGE_NAME = "com.android.test"; - private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service"; - private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME); + private static final ComponentName SERVICE_COMPONENT_NAME = + new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service"); + private static final ComponentName ACTIVITY_COMPONENT_NAME = + new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity"); private static final String EMPTY_STRING = ""; private static final String DEFAULT_SUMMARY = "default summary"; private static final String DEFAULT_DESCRIPTION = "default description"; @@ -110,9 +114,7 @@ public class AccessibilitySettingsTest { private final Context mContext = ApplicationProvider.getApplicationContext(); @Spy private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo( - PACKAGE_NAME, CLASS_NAME); - @Mock - private AccessibilityShortcutInfo mShortcutInfo; + SERVICE_COMPONENT_NAME); private ShadowAccessibilityManager mShadowAccessibilityManager; @Mock private LocalBluetoothManager mLocalBluetoothManager; @@ -121,11 +123,11 @@ public class AccessibilitySettingsTest { @Before public void setup() { - mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext)); + mShadowAccessibilityManager = Shadow.extract( + mContext.getSystemService(AccessibilityManager.class)); mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>()); mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat); ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; - setMockAccessibilityShortcutInfo(mShortcutInfo); Intent intent = new Intent(); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, @@ -368,7 +370,7 @@ public void onContentChanged_updatePreferenceInForeground_preferenceUpdated() { mFragment.onContentChanged(); RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference( - COMPONENT_NAME.flattenToString()); + SERVICE_COMPONENT_NAME.flattenToString()); assertThat(preference).isNotNull(); @@ -388,7 +390,7 @@ public void onContentChanged_updatePreferenceInBackground_preferenceUpdated() { mFragment.onResume(); RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference( - COMPONENT_NAME.flattenToString()); + SERVICE_COMPONENT_NAME.flattenToString()); assertThat(preference).isNotNull(); @@ -418,18 +420,44 @@ public void testAccessibilityMenuInSystem_NoPrefWhenNotInstalled() { assertThat(pref).isNull(); } - private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName, - String className) { - return getMockAccessibilityServiceInfo(new ComponentName(packageName, className)); + @Test + public void testSameNamedServiceAndActivity_bothPreferencesExist() { + final PackageManager pm = mContext.getPackageManager(); + AccessibilityServiceInfo a11yServiceInfo = mServiceInfo; + AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo(); + // Ensure the test service and activity have the same package name and label. + // Before this change, any service and activity with the same package name and + // label would cause the service to be hidden. + assertThat(a11yServiceInfo.getComponentName()) + .isNotEqualTo(a11yShortcutInfo.getComponentName()); + assertThat(a11yServiceInfo.getComponentName().getPackageName()) + .isEqualTo(a11yShortcutInfo.getComponentName().getPackageName()); + assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm)) + .isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm)); + // Prepare A11yManager with the test service and activity. + mShadowAccessibilityManager.setInstalledAccessibilityServiceList( + List.of(mServiceInfo)); + mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser( + List.of(getMockAccessibilityShortcutInfo())); + setupFragment(); + + // Both service and activity preferences should exist on the page. + RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference( + a11yServiceInfo.getComponentName().flattenToString()); + RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference( + a11yShortcutInfo.getComponentName().flattenToString()); + assertThat(servicePref).isNotNull(); + assertThat(activityPref).isNotNull(); } private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) { - final ApplicationInfo applicationInfo = new ApplicationInfo(); - final ServiceInfo serviceInfo = new ServiceInfo(); + final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class); + final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo()); applicationInfo.packageName = componentName.getPackageName(); serviceInfo.packageName = componentName.getPackageName(); serviceInfo.name = componentName.getClassName(); serviceInfo.applicationInfo = applicationInfo; + when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL); final ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.serviceInfo = serviceInfo; @@ -445,14 +473,16 @@ private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName c return null; } - private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) { + private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() { + AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class); final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class); activityInfo.applicationInfo = new ApplicationInfo(); when(mockInfo.getActivityInfo()).thenReturn(activityInfo); when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL); when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY); when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION); - when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME); + when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME); + return mockInfo; } private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) { diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java index de7792c2ec0..fcd1e42c547 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java @@ -16,15 +16,18 @@ package com.android.settings.testutils.shadow; +import android.accessibilityservice.AccessibilityShortcutInfo; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.ComponentName; +import android.content.Context; import android.util.ArrayMap; import android.view.accessibility.AccessibilityManager; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import java.util.List; import java.util.Map; /** @@ -33,9 +36,10 @@ @Implements(AccessibilityManager.class) public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager { private Map mA11yFeatureToTileMap = new ArrayMap<>(); + private List mInstalledAccessibilityShortcutList = List.of(); /** - * Implements a hidden method {@link AccessibilityManager.getA11yFeatureToTileMap} + * Implements a hidden method {@link AccessibilityManager#getA11yFeatureToTileMap} */ @Implementation public Map getA11yFeatureToTileMap(@UserIdInt int userId) { @@ -49,4 +53,22 @@ public void setA11yFeatureToTileMap( @NonNull Map a11yFeatureToTileMap) { mA11yFeatureToTileMap = a11yFeatureToTileMap; } + + /** + * Implements the hidden method + * {@link AccessibilityManager#getInstalledAccessibilityShortcutListAsUser}. + */ + @Implementation + public List getInstalledAccessibilityShortcutListAsUser( + @NonNull Context context, @UserIdInt int userId) { + return mInstalledAccessibilityShortcutList; + } + + /** + * Sets the value to be returned by {@link #getInstalledAccessibilityShortcutListAsUser}. + */ + public void setInstalledAccessibilityShortcutListAsUser( + @NonNull List installedAccessibilityShortcutList) { + mInstalledAccessibilityShortcutList = installedAccessibilityShortcutList; + } } From 0052a226a6e2e4cfd4fea9cd312afaeef3a2da04 Mon Sep 17 00:00:00 2001 From: Asher Simonds Date: Sun, 3 Apr 2022 12:18:21 +0200 Subject: [PATCH 007/350] Settings: Bring in the new icon * Part of the new iconset made by Asher Simonds Co-authored-by: Michael W Change-Id: Icdc71193c78642c9253f34b04f8629864f48212a --- res/drawable/ic_launcher_background.xml | 18 +++++++++ res/drawable/ic_launcher_foreground.xml | 36 ++++++++++++++++++ res/drawable/ic_launcher_monochrome.xml | 15 ++++++++ res/drawable/ic_launcher_settings.xml | 9 ++++- res/mipmap-hdpi/ic_launcher_settings.png | Bin 9956 -> 0 bytes res/mipmap-mdpi/ic_launcher_settings.png | Bin 5473 -> 0 bytes res/mipmap-xhdpi/ic_launcher_settings.png | Bin 12234 -> 0 bytes res/mipmap-xxhdpi/ic_launcher_settings.png | Bin 18397 -> 0 bytes res/mipmap-xxxhdpi/ic_launcher_settings.png | Bin 29215 -> 0 bytes .../CreateShortcutPreferenceController.java | 2 +- 10 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 res/drawable/ic_launcher_background.xml create mode 100644 res/drawable/ic_launcher_foreground.xml create mode 100644 res/drawable/ic_launcher_monochrome.xml delete mode 100644 res/mipmap-hdpi/ic_launcher_settings.png delete mode 100644 res/mipmap-mdpi/ic_launcher_settings.png delete mode 100644 res/mipmap-xhdpi/ic_launcher_settings.png delete mode 100644 res/mipmap-xxhdpi/ic_launcher_settings.png delete mode 100644 res/mipmap-xxxhdpi/ic_launcher_settings.png diff --git a/res/drawable/ic_launcher_background.xml b/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000000..8c651550f93 --- /dev/null +++ b/res/drawable/ic_launcher_background.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/res/drawable/ic_launcher_foreground.xml b/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000000..4d28d393e2b --- /dev/null +++ b/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + diff --git a/res/drawable/ic_launcher_monochrome.xml b/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 00000000000..087d83f82b3 --- /dev/null +++ b/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/res/drawable/ic_launcher_settings.xml b/res/drawable/ic_launcher_settings.xml index 9ede59d3c7b..b58c81ff94f 100644 --- a/res/drawable/ic_launcher_settings.xml +++ b/res/drawable/ic_launcher_settings.xml @@ -1,5 +1,10 @@ + - - + + + diff --git a/res/mipmap-hdpi/ic_launcher_settings.png b/res/mipmap-hdpi/ic_launcher_settings.png deleted file mode 100644 index ddce98e04e44c4207af24023ba8dbf947f1ab491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9956 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 Js}nH_`5&Sx#q|IH diff --git a/res/mipmap-mdpi/ic_launcher_settings.png b/res/mipmap-mdpi/ic_launcher_settings.png deleted file mode 100644 index 86f3be86ab5479dc6b3ece055fdf38dd1a81ea4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5473 zcmb7IXD}Q9(>^7~38MEJC2E2Q7ZNS%=^PG`J4EkYq8*~w=)L!za2%)i8uj!N5kx1V zMtSqjJM;bjemu`Jv%539zn-1l2rUgI5+Vj7002OuqAaiTPwoE;Lf}7+vosj{Cm?5K zLpK26A?1I81IWmt`xm5lSAexZ?>0DA#$%ydMP#kzm&)Uw+?Gg1X2D>K9FRW@tmOxf^*r^Gjl{j3F~HgtTrta0 zx)v^$(yCD1j*>J)Bsm~bS1!4tCs+sa>2V~$`K=DUM_CekJ}{$%kB3LAH*qxZh5fvc z*!Lp1t!hf!QejESRfvDBZ3(8|?v}=C0#Gjv)GKm_g zIteB3=KCEU{?)&MM6mo{n6Y{Q6a-BApxdJh(@; zP3gkX>WL4-J|0@otlJaKW#J)E0ucSpF_sHxkBOfG&be|vSpB0sk?|NE%QmVxW1wD4 z*mGUW-7>BuCzOt845y5v%uSj}N&uDHK$P8F?y@4HL%kwuwtsZ z>e~!I%6VN&C8vKV@bsPgdaqp+)g~kr0Mc=b5qZjdH~(gufRt6t-L>{LYW2;f%5J0ca|nKYmI`z%mijHR)rUriKA^@Qd)Yv_Z$dCm8?Nfz~_?uQE! zS^N&yUuT#SPWT$aU}dokofE_b(vg#F1IN~9zZJX)@B)_}m4eQW&K75Ms%;@l6fcz~ z&!?>VHPIH4j!DoknA)GCHTY&zAbi82;9YaG8Vp!xZ+!nZ_+p!6nDN-wZNCOp-0bFl z$i(nYUqj#TRBwo7y|Tf}X{iJ;>EUY>R}v3}YMB^j>D0u59WH~@0i4XpcL4{rPWA6K za=pjcau+aOnV<0Ku|jGOl2cQ4jp-P$<-!+N_pj@%;tsuv?QSwM2Jf;lkoMc_O!W|j zrKk>SIe8M3Ig&Szip+0hI`@|_2BozrZB5nPH6W~h=!k-Xwzi^yv7!OtSGk1bKD|#n zK`-0KrC=v7%p1J;%#Bi*Z-3T1t){=eVCC1ZHZ&MT+h`BJ&(>GwR#*o6^~S$5cqeu^ z8o+#ax(4v(weWK=PRep$JRloMM}3;3@_OV;KEjDJ#Dm~aj7I$B$irrcrfzlIh2;$V z@qR_^hIa5dp%uT|USnBmtharn4W$?^wri!*lTIM&BLpaYZ-eJnnOjP^?iKm_`H-lG z6#43)H#=(4mb0$_f(7oCMcyl;-eISv)})nc(=nw~+O4Xo-%?9(XEPtJ`kiM*9in}f zVi(N{qQPqKpb&3RK$0>%iHEg)fYZ1CC|qP?p5`xN4eGm)*J?hX`@kq%!TovS}+a8T&m<4KOD0MS5>t<^?edUn1x3KNMKEb~+-j8=M)?K;33(J#?f z@4Pzg_whoCKi#GANPzd%-|itVZL{8@DS?Tn#(^;pILdPT_v6fe1OL$FWHWLnvPpk#%VjVgM{5WMU5m1WAno|jU@uEZ~gxMVM-aj5_eo}+`Wl9Y748+a-G-; z=afPNSM(!a9(ybtR!?V<;?QOEo)s6*@Fq%N%}O{0*zEcDP_$MnFMsw@)G|iR=2B-IzsROy}z3}Q!+WZ!572(nvAX*(A9Cfm);aPk^^9KLh z>od-@OjSP-xe$Qvs1|q#UiULm7Vlc&dUHz*Lw>ce%}CRFY}*CFClYCF~}@amY(cQRi>vHTo_<7Bw9LCS=G<*f+yJ zTSFJ^=-t8>)0unH;`eP$5mWY?8M5D>3QbttOS$qDiq7oE$T8ZhmgPaZIQds_fImmG zo6Kz7{AdL}+|wWXI0^rV;^=Z2s}o_OBe?waC>r{r&WQckrga5xtDpbxg{r!*lcNQf$c!HF8rM`Pdsge|8!lEkZg}S4ySfria4M$zY>INU1fq7 z`qg4YOUKv_f;LbBnRPMa)fdvt55Poe;B&B!;o-$=$)6nvY;fmLGHB zF^{RyH<|9b`d(3UK_A)3d0exHcRqtMB1WmX&|xA*fq#&8?qj*Yxrn>*v!1Wy$U*jj z6M4%P8N2$&SwTU#2?yXhSQj-}>j&Q8z|Jo*sH52zK6zD{czV*0d@hoyxqMdJis!>v zWLgh9SXLxRtVPaUP~<4a&cgZNAt18MQnc#M2~Vc*fJR8bH=?gwz6rcetm~HqylL-> zL5&X+5hFY()%UhdZ%dfr+|2Lnd!=^s$fyP!tJ?DI*zF_1`RocP{o(vDq#KV`n21us z(}_i(5MhB!`T&XVCs`NJ$HrsSLMlV3i`%Jj;7I&NNdP*GL_5&@|#%eSCWZf$jF7lD7OD$wCI9}-2?DV)`mg$0L(SiV| zp{sE|488OW9GaI3|LwGqA$c?i&jMd1Th&TFQq2Fk-I^|ypnFhm($OE?cRM8{WdM53^2S>RIW(*)FWm9Ec(T~sy> z|F!stE&FnDW55j3N+LHdoe^Qurp|F4u`t(om4C$B7JD#ButamTigD}dKY&XKnL7sd z*H=&j+vJ-}jFs_?Zx0*7+*&?qYI=WVJHPY~z!qqmm#!8RMaVY1S&k;4CFqO~YZWgI zh{1d(0qPm2=E z7@(A5qKIXSYTDHgGCOu^FRl6_)e-t21-SRF^wZ1DXBt^k38zQPXSa#w1JXio$fX_E z=OJ`I0LmnR2_R&$s5SjcC-INuzT0TwVUlS&>=c;j$@Nv~ojZ=ks|0)Au2B_Aap9F1 zHAoNkvU~hc_>y!9&B=0&SW7|b1>Akh4l32Qbg5txH|=ibR2S-(=QPbtIU!L?73CQg zcU){ODmKTG2U3>!qJ0;8N=f%}^=JWhu?c9y9Y;&W-FL?euk}j}|F>a1F%LW5-p68^ zLTjSIFv$Xj{Hq~kDw#F?TIYMx>GCB1k*{Cp+B^2M5khIH!)Tp~39_FnoouRMEjKw9kD4ijki`ZXA_K{r+r&&L$#yrzzC?%DwA zz%pT-lp*rETf!TRX(zU-1iC*8BMC~?+c)=5wr|dxp#wFwt0?=@qDJoa=BfhA#1THp z5a5G1TPI{U=QqNF$r-J`7k^~3Ui;j+s@%Mo8p3rT?W3P8=0K};I&tCB*$_1}mS_Cv z#`mX1Vu+u?FSnvrFdnb78}JrPzFus6+19*%h*OtDJOd$*)%{V=ysnw*`68(&C}py< zxuOEHgAwmHWLH)(J`Xo|F^(YFMJ7}}XwXFVy< z<1@*#5S>*72<^4S%;i%#464)QUA}zyD<8yA@}hApff%Na%3pt4sN$!{xzTr{EG6c! z_*2TZmrgz_=}M?ca=q0}^LmWl>aC$MW6UTVZFUx!47ayhcH&Lt&tuQa;xT4?Zadh? zSc~}?NeMh-O&<2JU%pw{O&@O+#vriLO%xRi%5=`s6sIjy5e!zbSE=o^IbK^kfk^4g zfT&d;yOXcu_bPQ-de?<=p$-b_ILj(UDi|p40M~Vc*asB32eeG;JPTJ=WiQvoogkt_&^ub{%t;kkf z_k1IxT0XpQ7q7Sa>6bmnVC@S@9ojTiJlx~D2c2EQcnzwl)r7O_?THS*ePJpppdk=2 z!eNWY1f)=gG&B=3jU_?O5Pcp)xPGr64^sJ3W?Wn(iOxRaek=RJpz6kVdLB-4kUCwg z>nO5)=ec*DJhysztedo}4dW435MulhMP9yuSdA~h0|OqLI=_2Ox@e`s#z1tFVh4=i zouGnPyl1Ft`PEXLb(tb)JQL4e5*;Sjn*>?tC5Jc2 zZS!O&144KfLOQBXJJq(>fW$twnO@uJ48znjE6oCf<>K%iWF3r~(3`2$+QW9|lxb=q zFMfAhVX%_c_=rHZHuz{{+uuj)D)R*eLEkgDnK45nev_^~Uw1q)Fk4c!WhlPOe+O2R0BM zK3$6D+j3T6c=k{Wgga@b3k$dzyfRI`@zpy{41Pe`%dQvB8jsgh62vZ!Gl(D(yi~0$ zt9rUWi*l1oPZ+M)mHUNf^mF#7xgWcA4Cx7#lIuDy8%PrTp>kOelB`%~lyoHZSj|js zC$D;X0zZFIg&kl2sR~;ZZaEmZ+K0KI=9bWRQQ>VU7P}N+1keuqg|CM(A$rCJw$4#H zII5^)%sCDWZ^D+YrwUA|Oo4R9V=g`Ccy9G<*`OuB#LXO$sl<}o1`q|!KasNO#{Vp! z?VRyjuOhii$06lLUGEqN?^{-0fwqQqb;%dL#NRNPA!V&$@U`%ke)!Zi1Uy%;E*<2; z0fC`IA>wU%Xmy(!DT6ESTN~`BMD$2i&IuMM=^w@z86j;Uqa>? z5cIj=*uh+l3F!2v0>=}W=&Z=*;hp*doJkw0Aiy-8Of|`@Y24{KkWR`ATffa4D@a08 zV4!EPUe5N*5=LyUT40#u{Lpc%d)(17d)MWXw1E#Pp0Yt;%WC!3jg@TR@?56vHcsrz;gg8?ue!WUCb+OX2mj8+B#?D1B!#Mh z8+I_oDV9X3aZOp<7$miY08kgIx%v`@-jP*(53YnJ;wH9qhmGG*l9~o`5Mzny%5js4 zV9d02A9K0g2Skgl8}K`Cn_u^LMcE7mO--RI^g4IRkjwy*qaNjr-VzExrD(1J3Ii4h z1d})o+gd)R_#mKMT&BktBBrFzZWAg~ukr&$y;Id;jlD4q!xzArm;z~KB0~niJBT3E zqf4kVkjA`!O8aa8A)7!-00YXX8iaD#)Gb<+6q3lQT1_lJu9FZ&*x*;I*E5oI?H(hG z0Y)WwmRQ1w>!B~Ck6=N0D6wkI#aCkILcQ8J^Xz%W&oR?{N$uqWG5>xVQ)==?)grsj zmd9le1W58ww)+})i5i@Q_Y-F<-tio3fNclY8B%VNcgdvUko6nB@8`+0xGH|LDc zH7Chrl3d9|swhdLAQB=%K|!I&%1Eev)Jy+m_%9!2BstURM}^=hqw4|%g-i2a{v@kL zeep3!;3}!(s_tOn>S5w+4kZe5Ff}KawKK6aS2H&Oc{z=k3qnD$Xv<27YIv@k7{Uc= z3_Rv%YBw7e$iw}U6fKSSME_>5=)At$$3i1D|RPjlp zg?xMSomf3hWdwHe2FJV$m;ah#Og4G9}NC3vq|4P^}+6d>LM6n#_A%jC_E{Afo%CYNv$ks=nLnM$yoA1lYQsnyi`^4xpn++`>N3{>MT0U{#G z^oNf<9rzX+Es;j6VmL>!`Yiej*oPHYXBjQ;Pdh9=}2UFHt2v45w@toq`*@2dOVavWy1ojyKB?f1vWN4b$1c*FJJakKsC z*|yswrxWM>?C&S_bfU^am!YECQ)Y2Fp0~FG^Xr;0Hn>ZtSR3CoRC`j9tWmq~xuq-? znRCB#IAbJns?vjo8rixqtPOsBgRP0OLwoX#^CleqVxz_Cy0b=|RP~f`^v8lUs|~hd zpg$68>vz!St+x=k0{d0;(16CPQ8FvP;-;{Gg>X<`BxEhXG_hI*7z&>i1j7Jc$;!j*{8)-Dx4s z_2aL07BS8Zo2dwJj~+Je3!SDL9`T!m~y-a83wOtE#OoB|mAzFbWFq+LykB!}T`zN7c%?Xn8Cl z_pvl4I1_lS?@(6Tg2?`)q+`AH2Nje8AM8bZ1^t$?D&ytf4e3h>Hpj4@psS5-cHX~G zxR2JmuKs~aG8$m4r8U^nCQX-amz~AwiQF6pe`pkcu&ZGlw|LA)AodfB@klampfger z+Xc1?K0H!`U8N-{@|n%4@Igau(odD6GBwy7=k4`@s)$qZIOoV)honyAeNFc$&hpEI zaMnhXsQa)FMgofKFraXwp32#XTyb}hwZnAZG0*l<-dqV;023oBi0wTW4#^=m#BD>K z9Q7_j0M!pKo=ltd-$FJXMJRvS4k6oM`+304C5Ocy|CXH5?+IA(=>EMJKxfHo0l3z} z)by6h%hiRN0XJ>$&fYZS=*)$t^Jo^vjwDUB;WqhmkW0&HN^l85j6p!x?S9Klfl)mJ z_rJ94%`BN?E#+_YI(omiFGB6+f_d0g#bdBC{n?5#cuh;$EG|Pj=*2o{Xs6br2>QR^ z+JB=|APjV)>j+!Ib;^UudhtQ=!tJ=smltRh*|&!`5oPEMcN6|@9Wl*6CO>v3@-T}; zt3;*NgC49}s~{uc3G~YK_Y$Hw*;+)4q-qf>2-2`FKK2bLkIbB%{+S_br^ml}^z^CA zu-o6SL4$p7xBRpG!KBFM`Tb@PIkALse{Pyxx4xd<-DoDB(UUplkk9jNy7K8v>yC!u zA{@JENwz`GL8Fq?Nwb~aX6Mn)JYHVg#TocD9SaWaSh<-NKcwE#Ee(ORpbXdr$tsu0K-e(CXc~=rspuDYfmf zc%03^?>1+|MyQZ7Li9~Cu|RjnEP3tu95Z6Dh#^H#@bhUk#IMGR9fGPe$VzXA3>$<`Y%r)xP&%cWB3B zX_n3%UNpA`@M794ev-c{FH2NEMM%|c1;u7JcR2T*+pprle)_v)Cpg76;nnbVUUs_D zj8AmoGgJ=Y3^GO_WI;NrYP=((u(RHMT8f?4XH5O@UZ8{FZts!8Nh|Hr=t8M0psv#8U)2_a z(0ZQ!)bX|Y_?E*kO6Oubj>wzucZhf0tq$*rD~eGDvOlbjNcP7c#@tRe3 zWsp=BcItSc)4Bv0M$=n_QHJDjg{wJ{NBjz*veA5 zE81i1w6__<=KI`^p<#tBm==|1Awh4=lvE4dsjeWz_KYj%?Pf=Wfc@bib6KMHmZ;hP zY2<3&hV11a8o(91%)CK2Bd7|GQL9hG+N;#>dIJ8(YqEJ7@XPxlUWwGZ_xGpHhC!3Q z!NHA;rkj@JbAO+T??fFYid9SDFE*%KDG{mbd+Yh)^cwX5iKUtx!* zF^2$%>3nH!))pEih?gz?NB=!C!tbRSV!$C8U$t?IOZdg4r?F7 z{C_TM>Mwn1o9o`^bCzY@4G+(Lcg)3)z3BM3NV`Ry9?>Pig%<2Aq2F8}0Y z`_T`%XF3zNJ2f}E1lP!5=N1YO3LyE}aL+RnNES@^U58{`tA z72vK4nNPI}A4=#ELbaJ9{#sXZ`wITX+-<{G2DE2>SWkqHIphc1Rb%xRy0S&{Nw5fA z_x~eVA-k?Z=4TB~KJ(pu`&u^PXUu~HfMP2Yt4A)?K{d&*z?^2%WNaI-wOL9~ku1tz z-t?qs>l4Q6s$))7XrTaJ&n<20EUGndt>gJSOj{DUpS@U&Wst?<*IH#IQiMytv7I6= z_}yGSIZ%vMNM=x_gVE8jVB+g1`8$(Sqg$`C^Mf%G>d6iENpgJelhMYFZ{``Ewxwt8 zoM+o~a;xp#Q8U%*p8`o5_7041FAuhl(_HMmd5%>jzT8rh?{ldlh4Ikl*BQO`{oR4H z2sRMC3!c>db>=esW2;<|WJ3Kmhy#cxm{A7+C9cAW--0U zo1h4$(2RMM-^uD_YmciU*`I1J z%7F1gLZ_>RcB>l-9-T1;=N&B;D6ZQ5V^BKlC&Wvw#qDQJ$J|UA903C!(kpOgonW)c zkuC%8O=tg~1bT%iPMbFCI}LwGMK1reI!sJG-TvdCAUR-t@1)__XVhs&q)1*-BFrZX zizLN?1|htI*1TgcJ?EDNe@b8H-ZFRW%)lll`8x&xCGHXH2ZB$YY> zTBYbbO!v-IWT^8I{+a4USzM=02N9kVh5^t>ua`pZjr|u0L>|o;Cc|5|1ANaE1LkkU zE_?fB!QE@ZUGID%B(7H33y)?QAwO%)1W;?2wDsyeAETJg-yZ)ERPABm7@7jd(r6ek zsEilNe4q(Ds+^qX#og=0qNVNup5x;^$M z^LJmY#iGR%FI<-fZqBB!sF8YTIh_Wh-@riUb-g~j6W&Jyq|mCW5&bXcpVo20Q;=2l z!#ly%r2R4qD5T*H$X2Bde$RJ>PG-{aijfmmx;-rx0T-i&_npG(JXhjStmky*_zMGR zp0*n~P-$v`d0#1gBjWiOW)A6>6P#T1<2`px| zE7u}AjgQ}S|7jN~5%PVR7B*m?G{4QdC40HfY`uOQ>svpp_E%&UJF`d2?5TTuE_F&* z7Tgo98#>%TSN^)XxWmRDCtULGRU~!8oPGOel*xV*rcDW*&L#d^rV=Wq-QwBI=OMHc zgQ*&up)OhX!_c!|$MpdUK2}>pGG=l#Kaj2Y-8^ei(_{_kc)qcFp~rMy&?-Z6sPKuT zO=zY%1whI((IM#nxSfbs6d-4K<`{liI$g_+G(5v?x7qHn004J9vGB%tQwVkMD3a)WbCebx_^_a|2`o`Apj8SQ-B zur-rpu?FrceL2>uc=tz&r;5wy*gYBVd5-#|B4ukvi|bL&eU|}3A`f)A>2zgzFd>Fs zfg)=*+Th$M_z(fB;O|Wj5M_~4`&NKK)`FhJRQ>h_|Ho@Sk0nd@1q4tlsAKe|$R5{f zbolQ0v&fz3U(8@!3$)BTux}gg=IH9TVVi`CL*xUg4U^+l!Q~U)qJd4aMrNI5k zy6XfA&udplhpCU*@w@4IT1mn(2_dton}x1APsbq zak4bG7F>;9Xr_vs9p7qVi2YOFrknAJ8e#Y8?yBnDchW;-Y4wn;F99DHpiqyXZns^q zI2kokq`^9EHK|pWHSi{WY0x1nyRQvn)BBUUZ}WutZT~UdsI{*C%UeoC9do4R=IT4^ z8>-w*2gn#nfL6d=@Oi`SU0z;yv6k&t=w?ZNwriDW+3=m0tioYrg^D1%7&Yk;y9WYg z`}8>J5Au7f~frh_wq%Gj666$hH)uu_idAY zO1GExkwgW09YY>iE6?kNLNu(`*Eo^^r#i2ZI!g^^g=JjP_#=E>5v=Jxv5#Mn-x zN|1E^s#M(1@AUy3bR!)fCmL8-NYSC8>BS0YoNm;mK#hQiWlu%)X%m_deAlJusDAZ+ zO^;k?wHj-Y4E>gU;d{<_Ny{`YK6rv+jV5Z3x2(6y4O>?7^yEb{wP2_p6AycJNxKV% zju;#(t=Yt-S)+;T^@9R0$yEZEBCf;)A)dPRI&&gleuN-URt}^@9&72!JUOQlh=d}jkYwFIX$)>lV-^gGROwm91_(06HPZ2ZP+nGA| z2lBAS4Ft2XFc{747fMRlf6F0nl~Je2R0C_QX-qU`ukl?AlGCeJSyv8K=oQB~b0%4( zfJ!NKq{3nt2$(trNsZPn*@&!B0fE45JgD`g2c)4ZGxYvwFqbJ6b|_KeFMwoek+M|r zaS|SnW^sfjwFEYlN$+PTbT%%Kyur+~42r?;&X7VV+=?Ed^g_~zfdFZ8P4Qf^dN~V* z;62_ou71LWG1>6tOC(_jS%g!!IaP|B*Bkr zsnzBK9q1T!8R&ztLKQ+5R=dR#^@1>`mhcFeMiTy>90^Lq^Gykt7Z-A)HF){FSG z2xRofaBacT>K^F2rWEB-^XE=qI%g5!MihgGqoiA(xj6jZ{K{zU-^erK?mc_M^gd$AP~vJi@O zfD;+C+FXz3_@?pff!+3gIxqar2f_;(OAH%C#a<`5?NQprPu{vD@b#_QXU}@auGC*{ zjj-MWSFGlG0YX>xzE3W^RA_EHaBI-n8TjH1P@N zy;_XF0NpF=s5bcdJ*<6v3*B;fe=XAA(kZZ?)OwzN@m;q|0AmX)07;Gf{ zh+IgVTC8NnTgl0d(r4X1Dkh?Dl95{TZJIqmXf~J3f+O75gdWPlzV9XPkLQrvfdQIq zb+?g|rD{~IbRtntN z!Huwd>77j#MxH?;E)JJTA`VpuIO6*~%X24kc=>XU3JtmSr4WH^CauGTT`U9f>a{bv zn?)b&#}g_$0;b@gb%>PTWEYIJ zskCqEt5Er292;C**^sep89JryMYbHs_+nQMhDV@t)}VSb2vhs$-swq5kG?uX~qGU2NuBKfpKDf02Mf(Ypoi-umd`GyXW!z3i-Gsi91 zuj{ENKg~u-T@DlS@Zt%l9VV;!06Y;-t-%n^PdejBE}#8W#dC>99Q})9L455{n^Ohi zk4+?VZkd585zLwN5Gf2ePrUIgC*AP7A;G+HU;HLjBMd#4nSU+o3a}h%JCf=D=Fq_s zgnamW2VtW9Hj=2jiufL*PKz(TFPWX*?g9l}f>#RlQO&0Y&%J<#&4A~1b7P`B+*Eka z-rh@K6ik`#Jno{;25UO~s|vdk@2zbw}8hFH4y zFSU0l@Dm9p%YcxD z344qJw)O$x$Y)M2m1X0sNvHJ8KDRgZ9?Dx~VP4|m2(;IfS4>+7j^AEupMGFgQ(RA| z$w6{xbne;A9-^_!E4K~V#-J%@^kzH8HnTlgnnvtpxLQq(A!Vf#?N5=G2lnO-zBJRj z;&^|)z(|^EKrvYbL{83V6+W->yHir4H1F4wPgZ;ijaK4i1DPs|(kbj(E>=)zvlWb* zznv`}+;;tYaxqpoxWxvyaf zoKhTMx=)XiEOsesvw3Z0&UTbyod#xwY5_L(F~hBNnlQBW43CIFIegD{lm}}g``$5OFB;p%UT?nWAUMnwkIaVbRPbz&7U7RvN%Yn zzpX%se@Qje@~FkHkgp-EG#Y6mmx2`E9c8UwNv3LQ)-3up?_$*r%?e{347GXBTwX4j zFABbV`EAjdc3m{s5L65ON$prSlG#*2NmQ*to~{H+a)N+m&3)v<&EkJvJDP+5BfdqZ zgzq-+_+ul@1EW3}4_hfCQ1T%Ojda7jUnhRi20y-##=Bm&VI(Uz`0)x+OkP;`PGt}~ zg(*?8UH&BO{iP^C(Cd+=%vDCy@PEn=uZqKLRI=Xn@hBaILqwx0mV9@(m0wM^u#ryp z4*(yMsKOfKw{7+NTX|(~%Sm6DL$m$7wF>Rzyk>*YfcbTPcGqDmfdezZ9dG+{xK^9s zDAF#ovg`M)&M{`;l0KBhJSXuX)S5vHwI+j;SCh>j)0Rd7Bvkl48w^`Q;|GZQK+<-p z--$TY!Zh++mxMJSz5~e<=FafF=2K?>+L6H-L+mw9h4>n(Umm*`COaJVSTpG&BAbiP zH`7Cg!cM*As8m00p|5dkm!|`{!CAn`m+KX_fTzC4ZhVhFQ{4@J*A=kF25jE_?h`d_ z-0e~}#FlC9bl5&m1b!X*6wY*|Gz0RP_c)RA!E;gSG3|)(C8|J|FBdcQ8PM7p`@7^` z41CM36Im&7IcR9uTQih&AvW;(3!Fcifkf39*L;Kms&3owI=EdP^OpPqxG#6(KK^ax z&2b#{SQn}eDAm%*3x=%A>XuH6Xwd@UCe0eHO z7b}=D`k9~s6H<@_2;j5gm$3ayv_K6-eQW+gpk-vFq{H?Sh{s@n z3P8uo#)D?m>Ie${+_2x_6JIFd%;vDQZK?GJbHBZASe=uoeN_YS!0pqo7c&tn zr?aj;^*SXpS|V@*e?u8HRx*quO%ZF67mf}ff&dj4x4#+tb}GCN^OY4;=VVaN9)}v< zr((X*=)Y$w7EvU$?q}+6#PIh!5}TTC<5%+%&IHz4T@GSf=yh5rBXj8ob73RBwRrBd zv2=UWByK{Gm3*QCKJ*bz!@%$CBO%}cbDfz>VP(nsR||_8=c2&Oy?Ol*)CGC#8+6e#jCYsPNO5@$|v(htC3Q~izd46^CjBr-pgsHB|cD7!E10) z%X;$}z^}h8T?LlDTA-lkQ&IAdgT_^qTD(c5-h@_5fs)`N^ccJIj?;YrOUK`r_9K2R zO-;wm{hHP8hx74P`Au)jX)VS3A8vhkm^L;P_&&-t-_18<`<`wyUedE`51nk&u^^Wl zwwsryEA;n5w?~!$9y=aEFU@cx`~q%#{VY3Jzc`XRBiA0~X9M?3ud=&;>pT75es7l6 z?o8yqZ($|BIXp$L4MO{_A)%ifNc1TgrL^zV=a*d41_N!Cu`EBe63Ta97W35@s@SjSKE112+(h`o~9u>lO;Gs zR0?>k(Jt#`^+`3sB?fK?iX|wN(Q2hSr6cViMb+DrI&lKjwi-?o&XbTPk39Yj9K@b2 zG+je^$G4I2>U5cHyhw7Kd0muva28#Vf5pEGgO&B8C@^?T`#vnejB5Jqm{V+xxLcVH ziUKyl4{ij0=LJcLx%&kB!KxH#u)mn}6&D8+?h{M~;*Xm^r2CN&%y4hgI1(hgB)!Ak zPITIE_@=J%M!JQ}dLJ!~w2U#cHA0p(9VWd321(TOUVh&+F2!>;j{_Wc-Nvv#OR&pV zr~X+4LrGXD;fSw>@~R{-9hzSsqtDpQs;fxN3q~OzB1sqUP{Z$AN)*8Wzz@m=aPq-? zP6-Ul%f)Jn;69oE$iGF~v&@13)X5rlEBK57qu^Tqho#?MNlY%rH-2aL;4?Z=UH`$B z7@KCkxxO?PPRyj>55eb{rB#ffkY*Eo3Wr4{SVN1Bm%~lHCdQL2t@)Jz$3=7Pb?5Wi z^HC2~N(ofhb;@J(o($S(FDt>NFy*g3>~i^*`Y!2~pUyb3=-@}~BbjWO9W-ne?OjX= z5Mja6==f301tBPF-v%fZ0}a9x6}&LwcPVEWD6@=IHC>Ry(eaN_1UTcwQR$%?L?cd| z6OUX$Hv_16=@Q+oS1Tf~oe&0{tlDFsTRnfYJmu;#U*J(@KQ--H{M`6Hj7^MT;>tbL z?<1S^3h)4blP^+z_5kmK4LoYMwqlaO<C25umu<&U1Ypyg`L>FsM|l|x{dACy23M3qVzKR3=Mzk&C69g3o2{NXumMox4i=|ScCH~yX1{T69}8^Ko^hB=x*&WDN;IeZsd-dv;8>)Z)dYNrQ4KmMom@#ea>-@C zHMaSAj1yXGH}DpFxn-^YtXFKPC2-Z*>~R4;u3P+L9kt&3BTqcm?{;4)ZN5r-(f809 z=&ca>V4$k1YWBWhs}n;JV0M_jjlqjJA?_5N@HoavPCVCqK`SFtBNb}*MONb1)Xx&C zb<5P0lv;m1(psTiiaULCuV4ZDX<~y$+S5ZTKYBk|RTvz9;mNMtv5y(!}{F zYKnv6{NrgnA9pp*yir{`i&Xva){YqzX{k)H@cbx_bK>Ah6(DP_cRcfjmMQKTE}DwO zN6DfaAwqyr9K$?MEHo0{dfv5_pCek!vaVu~imn;^RByfIzMfu|OuO-PH8t>8BH<4j zK{R_*AN3?dkW13BOQd;<*>EUYQ;!>#6gLw*PlN|!-H@Q553<|D{x>?bh81ZVU_3iiUa#{QQq{yH2EOW z`!HwXaNy7KUgQb+qjvp;+bSaHcl=Av-Dl{)&Be@fU@ zmdY?ERr?hbN?1?TAAqLR$d`>`!B`U$F_MO-sF2v76VqB z>}JIUn~x+(wnH70-&G(E&m6Nxe$P95`dVVeW)X@?2I1Jl=oNz5%LYq#V*~@?C@?&s ztg$p&>R;0-bxLJRDOSG*--{C6p_5W!d-!2{MQR zTK%qnefz!=C;J>Nx=eyb7z~7agi$V+4;EdeqIKuDs5xuTF78?=qG3bJe2rIZH8Ezt z7eSi4>c@;>&?(4(lBMn;Ci9pTpiH6Hlo*|>q$4;dWC$*4Z!Zt?%~38-l6L+sLq{NG z60cHGP9Ir1Mw;Ia&tH9P)q7NiypAt=hS2}5<#Eo8(Sp%zh{X~Y&X&|U;;P^FQz8~s zs>G0!s9R(4q$Fp%}as}Q+T%8$U991Z}fNNYg8dT0J~ zd!IIYqTAdsG;EwjJc(jvT&Ygf$8~0p{&81Nq@v`sq15th%*u`sZWrczPT^` zEBX%+G=t1l2Q=j22ZGhcZiG)Q3gx&Xtb9bSwk1GaC~l>FeAAb)5ZFyH5rdm6mEMY} z={-EAdO4T8ayVwmY)`^J91YABWqKXlz}RFA6oIBlEM+U;7+L(O1fe6NWNpz&88+is z3i2FkqTDHx%*;#D0cC64uewsB;}g+=w|FQ2s#zeS4=(j*7LNv+^dm|OOOM1yxLj(! z&)fe9EyVFyasI@_B+Jw5+yMv_c93$JhWE`Q2|Mc4z$aSlIi_Lcx)RLLkO1AnFZtnW z;5BXb85riKiju)N8{-uj23VV3IjL>NWu@vI>h$(}HE?MW2x@l$ET;rIt8ox1HHrc% zMuVs);^Iris;gEdOsM2g}4@IHXHZ&BMWR-+`Y)C8rVID|AY-u&wk4ETP zv3sybAc-0ym4KaHH5hg3FQ zq#m>4oR8Z+5e{mVsjmKzD;Y=3+?u9b2+s7u^hsP?Y9-M+vK0S^0J+xS{}5Qfj+h6H z{D(@~M6n!CeUcJ0l*ZACMRc6dR~vGl2F((otfH>R57xq-sQ~e{f{h@W09C26!zM4#Thdxs_~1Niao9US1l6Y1mI9fKq3fwwo7~Ihwu`GqDGmQpnmGnVbGFH8Sz|Ic)m% z-8*S3S%8?T`|?Q^@-LkJ`N6h_Olvozlt-)<)qf_Ek~kb#JmHAIuOK##X?Gu@Jh}n* z{=QTIc2v_v^nefpm?1oXN}q&`B;eO73JZ$Y1XSu8vDcnCsk}X=Q9$ zPjBrx2Ue=zfrap9*Z)8MUmo~%`)vy0n^G$8cuN!A{DL4m9}ka<6`!f8>BQ~RdIyx4 zy-VSDoWBjVIQCmpzbN3}E~BU>lcZ#Ijs;ha5*{sAt$s}hHiPg+`&=)@i_n238IRX6 z$WC)FtJRQ)p5-n73Tx8Ry@I51UR?+1+QOj>t{MaG-Mer9UH+JUY)z0&pc8#eaC!Bh{?TP{2Q8?90g$7QG$ z+;(y_R6V+u)6nRkUIV3U$6MkBUlU-H!D3z?^eh)ZqS0Djj=F08Tf)QwoZ!T#;r292MLYTXl#6tf zo=tOz2~I1U#1n%~8{#8ZW&_uJTez0KdT04OpnN*yWY;NFQLytDFQ9GU)(_)gdC_km`Y zukd5TwI^wcfH=+YdNLBR>C3)>o#+^=i{DnaS9IG!ZP_05bs=mgl9RdB&S4+Pj`JNJ zRjkjOSs?FDL{*0R&+i{@N#AQAyfu9(MS(%^-UaS$AcV)a)^aA_v=#(E8_H3w(Am7y zmNHdhQdUV5vtyiPCp#BS@CQhqk$5EFt^Q}=@8hSPCrjK?NTjR04~;Al;21&U<6D!h zSt?4IC#~O8>L&Qnq%z(RklBol={=c3C#E|>nnNN+ALH3uHIbMl;Zs>u<2Bd%qs#VX ztL^w!M_M_X`+}9PWo!X0D)!&m6S`98ZJ~}vdZ*7+31`m){A7HP@ogibF}@Kp3Ek&kmX3@> ztZH|KUtU_)M>`a}b4Wu$^-Xko?;PiL-k;huT=jsGOo&c*cP z%9Do5p%PN3j8^yC^!=#P4jf1Iz&#{-wB2yx7?zZhlC>*^GVPi{Oq8LYsziz3i;oR*kE7i>4^ZHu8LP>%r*ebRD#c5cF|J;KCpX4+3 z+FK7k^p&S%3l~8ks|_HFwsGMx!3p0BsMFtYI12DLCHKdM6kh@NsXtz;EFtX-Gp!R@ z7c=aeBXEB?+fq4<2l!7bkA*YI3yOR8)=NdGI2YAGY;~1&)^xQl;mBJ}wB|b93LQvh zC|`^SYkpgo%PYvn9(hY)v8LQNZs-Z!AtrRW}$n%rjn?N-THrx-Z zstmF*Mv<(=aa>mQ;|h>^bypsmT~Un2E&Twfb{Q^WNV;x845KW8O(y&+(-e-br2{7B zI?2abr?|}OzaCe||0pEE|J^MjUn$Zs-4(eoAOAL(BX|>d#(#Eiu2|9hgpaSUqmOr2 zVC#Y#PH=g1<6fUPK>qWt9epGF{-8HT)Mv4dy|cDXDEjr~gb>?DeL~*{M+M%#SYAGc7%zmXEb`f{`T$=ESaMx>gpW;)*uNT@*EO?K9 zyobyuQ+d2@-^Ykd8~UaTcY}q;+lwjvP5h$>ie!PI|=%Xp;B`M!UaxiH)HsH70rr?j-RsNU{) zVR7uw-LXV;8YgBLr8GFqf`2ecZ60&?Z3xd94ad_`L{oqZn8D=ZX&$B?TF|W2I;9`N zt4{`_Ba!`Pe0)vq7Xvy@<{Lg65@?5?whUg~XJfbNmF^Of-HRiDs5%i4Q{1BmX>#xD zi`}ly%|gYuhi)_sfIpAI=oK#ReN1$dH>8iN_adlqb6U|H@9!Cm+Tbf^JHZ@(pf zikJI=4Yf?y%doOB#4@T`MB%e&VwB#vf93WI-j4&48oCXPNfhgTiv}T)!aILQpM5uI z``Lm{8#MSyJYLFnnP0qzle(ZLC=fE@V$74I4-!|pPP=ImN-r-)^tWefzR`{hV^sahEGm~QUM)Q9r<{cBDD5uR5GE9V;e!Uuc zM)df)e1F<9a;Qall3RfO+vZcwVL*)Kxl=*-+DBKRn{y4zcuQOm@CS zzW>M4`VKjrA{3RBX(8hy5v!Cct}z60I^eqkCI+UZSU_=FNEl#rbn+{2gL0L=#xI$q zk7O}|f2$=zPCR{X<8KHw62lxNz7GgW0Wd7XO5wx8s34{*ZX0o@D@`c%C+i)=MCdGi zTYDN^cUt}bd_vck)iekhNDdmv_*{3csY^psfbtwfWE@j}cK;~^P{8A7dM#7|fF!7_ zK<(nv)$`--mF^eH#rrY0Np@@3zHc0Xic!?t&Vve_&Wv~D;SvEi) zWrFn8cCqc-E}m){7o{F?B#fBCg*OR5q_kA04zMCM_&$5`QqvC9D$6sasH4=K%*yX| z2^%EuPE&wsUnr<(5CY>GjAQOkV-QAZ=@78(0pFwmHAnBoHO7XXh z372{L92V*RFwI=}bh7nmUc*PjLqs&MjMrovze7slO9Ad5R-VjIpRTgcS&7g?Ror~M zU1=a*V0kcsHpPhEj1aASmV%IQRX`e*$c(59m)XW7MMb^lrlm=F9e2RFc?!8CTIII5 z=)Ht5$3N$(_xw6{R;2WCjHfrB$D&T-+pz(dx@K;@`+PVfqgjS-SU6cOQD{Inb$_aW z1D!AX4LXV_2l)L5OpB+L8=SDUnDu8^0=rL((e}b`hEh!659;`nu<&?1Ng0LXT;41x z%nY);+)y!xxYGr>CRenPlK)jsweJVFh{hl{2~-G+^ObLHperSe`}=C0?vFmrt$`+;%~ zV%lMB&JSN`m|q;qRw7M&W3a?x8r&89uZg6Vgl_7wZ_{38>pJ<&u7@uA=c2hKm&onB zZp%~5S#<0_>}rxVgtB>kQ^*qxn2TDWapC$Xh9JiBN$lzRxus4`jnIH)Vbf6s>Ad}T z8cOc>F#lQWf@jo1T$EEJoUm#gG)sL6~I5=m5Eg!^Vc5*F^~;Uf7o(%r!NP z^(WvkK1F+0aJ?ZU$Ye5|R24@IqYh=vbON4QDiVW7QKFt3@dM@{f(b74yF$_ALkz|a zC4BtL;~n+j?fD{X8H6EmMD%*>Z%)>67Sgt{4kTVJ^z>W|k6)}uQskhaVhvG45HRa7 z!<}XflHsU3IK{nk3b`T;sIF6{A%g56k6L+bFH{JnXM4Rg?B;$ySb2X-F4DJugX67G zw3v5GKGn4q0mEZi@sFj%x@zP+X8?X_io0LJ$_v#<2;AKJgd95d7@hopM*Iz47or57*6VGJY0YoBRk~d3ypGT~+){2(Gp%BU@)&L4SV*AAI_VPImHC9R$*?Zj&)l zqDXAg_jy^pto`E=%o4(Fvlo(a{<6Pu?qLtfw){&ZNS-#_=h) z{VnygfiBcbPJ40>k}Gvq#oA&YajvYun^QWfQ*H@;xEG46_Ddt*$8+IyFN;MXoQ3jo z?ld=Ok`r+MwwW58T=P;?GmQyQOvn5r-Bls7ca)Q^ZY1Ass_=gLpLABIWH&L_D?oTx z$2E#{fYU}o{dDY?rPvxcQ)bZsT&~%0yZv)e>2dpZ`QW28svc?ubp>+;SA|z@W7}jI z?Vui3Vh>nMuKDNpDto@qzxD|cFo$@Yaqh1zHJ(ovPqSIzV+vnrJ8E{9Faks zbW`^M0gV@(o7=M7KT~mQB(I<=}JX_skxq( zDL+XeV*{GIT(`J054mssfoQO*!Lqqb1@ri#IldebBO8mwTR*0pa`#nbT>OkDdI+CN z@@I9XmS)-<|NP6?gnkrr=HpxSUJ12ctnezpIFgf2n7qH3ce7kCZFMeMZqdP!l*c&~ zn08_g5}fpb<-I;#y=dT>J2cK+9b1M|p{BcHA+-(1=1PPL^6BJNa-|4R$rhHeXiD~d zl6xvQreQ1{#P_({4DBQme)tuF*&9N7x_8xfvEX&4eQ7%G2)vdWuj>I10cpS=SqrUg zI_U`2Dbrh6Xa8CUB7O2fn+;&WUt$bX{}$$ucIP@X8{Yi7%pG)qorjA{Pi?j z%ewXmOE#N8xQS8p?j&kE+Qs}|BV&m2s{3)^x%+eAk)Y`8&#L_l?QpNgibv|8&3t1W z32c*reA$G|#ucnj0($5_WXyhoqqm-Vayj*V{PI)v)g`bN=Thx+GyL2WZ zZfi(ARzLVnd3PM&!D0K$D23qKXh)p5@Fb6DbJ+6l!?piyH~y%5w;i^Rez0-HO=L4h zUgZ)iXoaf=B51bM8PC>I7HRu?Z*FjBp%X88XmhlRiid~DM5Sk1MZDTr2?%IV%VBdr zQ??5SMoJmy65F@zS8?y-)GEwT zMRfr_Ewf4vdOIY-ndT!O=4`*CS3CZjonE^N1{kS{RhC)AZ$0hV2VoV8hVV}tVuPek ze_{$1g&qHf+w-!nsH@;(Hxv3?q44a1y}e1n*87)v+o#eBz;fS4Hcg4aRmn6HRd;b8!RHO zJwbuoipOb_P7q4ou8zaF6one_r?3}3b-yr_?Y~VbF{8p)=cCGPPg@D+JbGJ!%UE5` zHC;M*|DpxA=YEQQz5Ivi!ez#)pD;2hCOiB11wTEn&WjW?EPQaQfZ5geytzi=;kt6H zEU7sodN0QZdMe+!TCJq+#p`%FrntD>o1t3yj5~-0SZ$EuP$uzbD4Z^USBy3I`ck`h zA0hI5DJ|Oe?;&Jh;QUf6rS0730auIF{_x0GB-3lOLOP1rqmBW>nSRAV?9_B9a?p7o z5<|jwnT)=_?rp6BD@om#n-rMT!_g_!`*++mKPSaIYNb`(E5bHcFpw`gwfp4(6%Vwa z)#6y7@+1)o(S+m;1P00^x`;;@eK(*#gVWX$jr&!B^-2lpy8MW?lx#w)-Bjc|+3s=f z`}3(UOk!kYq*WOEdPL0{f6Zm1%${NzJG{->tVSwfo6TueF*;@)iwd(%=No{4(prR2 zY~C0V=6y9!oP7fA740ljH3>gE0543T{vDcbi23w7l_%HTu zHf&QD*9QG=g9Yknq4W!>>J0W!{*{h8?45s(;mgxh)(-LS&ZwVq3vzGMybbb@eY)R~_(XsFM4{E~{Lgu&8`gH;g5r~i? zz=v8@ise0BtfoyP`(-T0K^tHli2;NmM%3~=NM9{!pQ1n^MK|IQ?blVW!~ zgH??WtVC7nT=e9h?jr0y#Z2_(c;n?+aeT#1NZk210wK7V!(@K6oE%6UZO!hEwT4rB z$f>efUlnYX+&9FTyAhG&u=Q1_7E6a1P}p$iQa|<3ZIFzU#bK@)sIkv=$STZR@xDU7@X>uMjSp|HHQO2Ibx&0IYt?qkW)RY$ zq=|XnbO{jW-G5)?2Ep^XyzQZt_-xUogH~g3NxjbL<{`?R1X+dhCXOVUCFwL#yN(mTnt(lw`G-zE)(uysX zYFq$xQEP^4Nq*PtwpwE2en*DQYW2+XmBYq@17rd8|7eUojsYHY z&KbX|jyU9;jk~!!@XSay7Q_c@hj;$Km&5?1P!EUuo(IN^jm^KL_}ri3r}@GlIf^2S zipRZEty6-gPrZ#cE)xhB>~6vW*kRtCLt{!DD3x4rz&A)^pE#4MxkicgHxb7s>V#lkw12 z`mb|b+At)*%l#BIApG>SU$|R1Kw!^ag-*_CYhyiv8_+EcBY!1$9L9r!_D>PV#MCw# zAMS7lw@c8{Pc81Tt>W1-i>+%X|NWTYNPwC~8_izg?C?%d;g^WFr9uhQ)Swt@lVGzL zMpuj>nI7wJMihe|kW{RqfS-mN^UNoO#W??k6+q)Ei(oK|3Oz+88UFr7Rvf>*fFYa$ z{6T#D_H?wiV{$L~dj$g%VL`g6(M;}O>rVkS?V{m9a!(t&jRNYf@$uYcn?>2=T?O6U z&mUF4Ye?nF5>+MTGXzlJA-(((%{=uvE49{4Idt<%hL z($To-R*{1sA%p5Z>!7>!Hd{`V1vUaQo9jO5@udpU3Uc8pxPf;G6|{G<89<+B?m~N~ z>1J7KrI?^)KaTqMK8!5@@TwIO+FEZ5d5_H&rlV<^OnU_Idm3oPS@O_NiNI1(11F0^ zm-C8Ex_fo{P6D(os7p%^3abtHI9ugiEOIPmXi`DgBhd)eW#=mp0=8i9a6`)EFX?;3 z?#r4npI&qAgsUp&6oHA}mFkOef8ur;KCR$1IJ3yj$YxE+b^kzk6E&LrmvK5Lnn%*Y z4RmtYk^c)^MeAHnInoO0JMHbkIoA0nvD4@h>iZPq(YF`xTZR;i;MX=S01lD z8#>=+?Wvon&dx&Pk=)u>J&OchHR@;bx}Y!d&K0fBlHAzL9D4rsYvvdL3LiSf^qoY9 z<}pS&RmX6eaHX1fApIvFk+H=Qr@x#ql)JABM;fk}ne|H*wm(EZ6Cf*#!WNd3lc(64 z8n`NE-pn{zv*o7!uabf zChMtYd_Ji>mj;)XHu-zpi2tU%V*Iyf4Pt~aS=J842c8-o;Zk@UDQ1;rg{&$t&yce+z4FB{h)>4{76Xx&t zv|V&qN?x2dm{kA~qx^XDl-}6bevSY=#y{oJ8}1fnHWAuwmf4J^hPg5K z=QaGxHTC+>^=xTP-RN|CwZ04K?9a@8|2F8Wga>^Ku*DN54COS|b}N;TSv~=ST6CbW zge+cC!TJU0?KQHXs0aeH(zmZYnhi}g-`a0)R|(nIrka$`%MJn)t;JA-Asg!CUN_fw z#-+hf2^J^ITGZqh6iMzz(krRnwh{AA@5L(gCBG$(dy5OTjdBC`hkEPR5>HPaEJ%>% za|6;!a(K5~`DxJR@!5GUAWW4_&S1pWNZ%lk_(hJpp}DIlF4a&%!i zw)ZCFwNhvIjr8pEB`jq{_)I3un!DA5XbA_dR#xlk_ewHifVt_ zQsKgT&jct^>Ug4Tb(L{z!WE`_%Pu4eAAj#Ft^5``lCRqT=ku7;@wZ~dUXIj$>7gO< zbwZkv(t?AOnvhT?|3gM6$<3w(C6Xog{;cvuhPum*X-a#c z<+8hokfs|Dct+mhF*@Pasa4lOc_G30NhZHi=Sh^RHl?E2Je^mbCd@VxVc5dP4q4=G zNF^tm+e7?(y@M%qXF*XPHvNIxl|0eGRC2@nQ#p-#?7D{kV6CV7o9dumkP z8)KY*eEN5d+;x2BYE=PuyTAHNx}qlj(P#ZE=qs%mo`HYx7SN#q-G{!tfuh?3EC@@xo@Lf zlb!XY_Bq*6c~WOWa`7~@87^wsOjWeaSrfA<$(j&GpkJ{6u`=a90UeqwGe zv8UPb{TNceK3|PpE-0&TW9di-9f`}ILsQev7(6!Zx>*l-89Ay5s3pZ&D>#c(?ToD~ z76-!6!{CHhTv_S5TqBlDx|twulgp4FK4HPhfnaTN73a6+^S8Msw8QM-+pgWvH^ukA z0=tn!w@73e*>rDhfMCXxN1$u8j0T!;v805HaE!o1ES!ek#q99oIp0b3uMLg)G{H1| z_cgwi}|`<^TDWXX_Hk9%_CJIEv_jjS8zr1d$S7&V-dXx#vnkdRT#) z=6CQ{>^~_j-TvFFNZ#GHeHpZcjwBj^&%Ol8>2&*O@CLx#c*Em~;!F*`|M2AIEICf6 zm~ej?I7B?*yX*1R*{zU_OD-^|Y&(>meSHz+@Qw?jzzWE+7E=)GoA`zb#HQrm)5CEq zYs0dd)iSbCzMJVN(W89}<{lV*d|#gmCGJg;Z0n0yTuD6bPK5ia)W(?7x$9Ppx(_Yo!8f=yKlwVfJ%b#4}wfLV_#;ndx{ro`vte zm(N_^KwsarL#KbrmjNv@!|yV}FSFrf^ky6xCNL64-KEckEuHW59L8byCAcG-V6)*y zj=LYo;UmG}tBagLMUajX2PNZ%LIq$HVA}C+KKlo(Fxou3_i3bD(WTOZd ziCg+$y zk6(%lDlg|Wn;us-De<2suL!vDj5N0JL?^6@;H6Xlrb$VA#2l~<@(I&DK*fLPZ5aqc zX^?FV!`#~n1SzHTw`(=6aa$NxC9EykFU386eE>&%rfcN`AZi{wnfEe8EXkVIU^r~y z#r@{b8w;T{o3%X~a+zqX-Wz{I#5G>_XP4q-T(}raH0P6@&jY$`lP!)3mTM?L)C7An zUncUyaJ2*V<0;(aH*CsRYd)E68mwv0MDzYZBm461y~e763`FKw?pWbizZj&sF?FX$ zEG}cCBM`BbWS8B4R=s{n75+tq_KmDM3=HW0{l*)9m*I9Mou)dH2@fsM)GAU;L7NV% zr;>_yo!bn3Ktv^ZiWoLQl{J0YE?0_KX<>ltv+lQ38&9=#j^IA z8kBn)?PbQKP%-a-l^BX5_-1j8)bA+6={1X12>PoesUlK&U|+2lj5-DN+I*x?t|`ri z{+G=KeS%z}V8?5bL-K|0(a>n_iH=;absJ8u+`gtSRDGXZ`HXyTHl9pB!GF#kgQB_H z`_qAV5(EDQh02lC<EN=Ke83B+$?if%EZf|xhtc}-u#FbHal2y`gp?LyWK~t3 z_NSxQ7uBJC!w6K|%mS3xi#Y>3p{`|CmpqqT7p9LK`CsqHm|^bT9YhJrG&Q!$hfW_= z1tW>al3h|`q)vHZge-k5I$#JJCkm!9ds{?o&Wr7GUD;NzhrQ@>mqAw^N(b5j61S<& z_!%F$yYnq}RePg5wvfSuIHhc|9z8ijPe#Dl436f)aoyxs@1b4%9KozxTx~p6O7v`+ zr5dNj5#)_WXh9(bZ=a%was(?lb;?wI8`_9QGDXUv(1pMtiE{bfuiow_Q9Vn50$t@ofswF5F&#rVZs``>iOIMUk*vmOgNo+^dUHqi$^7nGCC~-#2K#5o zo*dJW_q;umbZI1dY7U0kBt}~O{PE69y3N;T|g=qFi~vzvX}3e&&JdKB_LG!t&*Xk z-&wz1256KI&ui89PTTkAZQ6FQg=!VDG-&0BIKUb*t*KJ#Q(XFEA+LvTB#-gEs@mDc z!b?OP)V>3D?e~=xHmpb}b_RNrwMG?!WBDd%)v>+N1AD{b`F`ogZm=i! zm$n#KX{liG(n>@V(fw(3AJ4pNO>I%9(KaUbVqc4F0OW-6AS zHd=Fyjgk=l_l$*aMHH)_^LKPGoIs2{zvV*8bz5c_sUKj{=|)KPLE}2OM$G=WVpNzw zD!G-__@KU#Z^)yWL*YbcKs<1y@O3P|h9^n%+t-+e1UPkjfLZK;n4C#|$y$OJ5G9x0`rAC7Uw2$&GOOlF{G)N} z4P#wJ4jL?YzxKw6EVw zPrLtSY)oAfv4x!p@*ED)Q$Un)`-BN{&26dKYZvnCzr8 zSN&7VX2@ZNOO+uxUD2hdA z5QE{o#cLtMiOfHlfpy&j7O1gczgkxy%&~gR&K^?oh|_k&gb#5^#RUdcXR!PU{adcz zu?*!+bflk9!3Pnwty1v?2J>(7v!dNR<8_F_;(j3Y!fjn8#iTvuS##TOkRn03lyDhxa4)efm#7UCkT4`D}rMSKLttS-{V0d{%Kqn`#lX8 zk^f3&KqK?M7LA!rm6%%|k!X&JGof;>PQ?KmG5Z{s7a(z|=?I3(+SAM=-7P{sj}bjL5wLaJ>ekG?obo*Y4V7IDlDi9o-8idfQiQN zSle8|$e0`p^TyGYXD-u|lW7jC9MYNUBa5ejZ$)X|CnYH&+Rfm`1e^j5mRzy=tVH7& zfp)7A^v`2BNbT9Ghy7 zkJ{bOcCfQOT$feUZdvonr-n2jt)|lk5!7dW4Iu-fQEw49PJVl(?P@l(DcrgnE|ND$ z^Svka!>3h@i6LU9Or9SguSbtU^P}-CL7J{nR>lK(<$5+~KA_e4l&d}-egBWuU;rm3 zKde3Dcf3vnlA=}jfolFP-w>y;8Px1$NRxTlm0037f@|>-aqTtL;WzsN6N(eS%gwhS zmgSQO$@Eb_k4>ntufTJ=Q^Z=*x^3$~jL5}S#H;UR!EU(rLa_y$kfdMui_p5Mq)ZI2 zm$UFmzh|Fw5#qg{Y52Ck@vV^IT{WdKuH;3-TlVrWfVLf-<(am3+|-Mli@EdrnK-z@ zKQZXWTpR4#tf-4|S^(cZfmL2evBd&5s?iCxjCjk{k}a+K1R_)U!y%jH_%;;Ny*_Ku z0m0idq3TIrzqV~<{8K)wojG+j4Tt9t#Rz<#c9RS8^Jcdi%X`AY4;I=1+2HbYDw*~B5>pT$lk>}hKfjUt^swW#I1n+%dkZG5l|qPlU4@vVYZ~@@2RSWS zF@na+cxa(9NTq*`u#ZEXOF zdZsSwqSM$eSux*R*6L-K$5Ru(9o5FGjlx%(5AH&RKALg{Iec=Vc^3w zCZi|oO|_LaR73*ZgM%Z#j4ekbovlMg)G&NgNd?U3`P6Z2(vRH_h~~$mx(&*t$Mv#B zna1h6Bq&SY1G|W0)a?@CXAl&&btOWO0uDf8&Yi?IOnlDv=;GW_W>Y{_KXAC=C6O~r zQ8Kl6GSa7|dnCQ?8NBXhf6C2qHC%d~RR>Cf^%RN8js)>t%4vjz^Xb;(^ z+rUSF!Y=3RzMdnSC>3WLC910~4*r>D{&dwKQQ6t+J}dpp-@fe$_I&Fv=4S!>?5{T! z_RMmUS&*a#n&mX)C<*}|rvOvRDZBfSqS)qxLc`Xq>Hxk;xROC8&}Yd;#s6g5K|AFy`ZrG znPc)XzFk#(eo*I$BoubEp{#$Euj;(NbLr}`j$Yg_PoeSZIWfXStE`H36NU1E1U^)V z0O;H-*mD)ER(DLrf$fhy*^Yx+dGXoR6N)r}w$@5AQH-lWisK1c53zR@aG=zz=iJ(n zZ3Lm5H=n~HLvHjK^RXe}USW_w0v+N%T&+Mt1FP`L#q;UsPZDf`k7AlTx5l16FH@^- z8&v`iLlT~-2ODmw87scKr413j9{sGZH+Nq1{f`&TahPy~3he;jUja@DlIA99=X?rC z8~SWcj$XBxR)CXj5GZCs(kvZv13Zgv(PHy+6K7isZc0Bn zx_I=2YXJ-CqMHy$g;W3#vOR%*QWlwdbJPoKbwNlEp;8ohoOxYo6>g38sQJ-14bP0- zV2i~BT!^4JUbROs%jq5~z&ds9RrdzfT3`+CnECjDm+}TQW1W@eGBHgu9imD+l&Vw0 z8oH$tR*?QoG!i7IyDU9CzAd@<4B@Q>gkiZ~+=OKvi$LCo@VD8|)t6QDYv@6bx@CqN zJBy7r_RE)t<_7KC3yt@l{prCvRzyupE>aV}Ib?rU>WaZLSFE6EK~nDc){md(pk_#7 zUgqVPOE9g1_@cjY3R1DM_>%9-rIwjL*na;5DYmf=ivX2!15^7RFHR9#=HF*t|DzAf zosuIsjafnG!<5v@Y5H4-zP8L(x5whHlej}5hebmfJVhS{2Ynpuu6eoRwUS^+2vVAdzYr{u_Y|_J zmc-WIO-)X#L8~n+*w%mC`xe*-v{ArNi`YU%zxw9syWA>cFwuc{Qh{9fN#dY z=9E#=<~;@6S4_e+0Q`wJ7BhwfXZZ?BMDbI%_YoW0m7Jh@f`<-u6!f3b3Hy98Ded|M zHjU_6N#z#5yMn~yV!^UFTg>~kW~rK%o!mBl_$WkXq4P4yebvI#49*;5r4#|dnh5Ug z)(Y|*B(j!pjR96v%PX`rjH_mYOsgv+R6$B;x(F@iqR#sgOIzp)~+Ko+K3d>F@!E-+0N@X-{Ei^YI=6#_nG%ZtL?5{nOzDCJr zFMZVn<-O(b_ZU}y!f}~1iSt@pmw%2Qjf`Y2J~yzKvF=zpDxfOQWjN*ld6nu4=z; zQw*xKK-s$9J4=LGg{Oa{l94YYZ7w+1e(*}C$|wG+0mJY_JTGmy3Abqs1n>QuxC{sN z4x2Hw;pUMw^u%vtJ}W+T3lejow6dvj&_G{AG{OC5jLAMG+Tk_-)}h+BM7kJ1nqw>U zOmfjuCO{LbF&b^BPy1tf^@6gA@hpvZ6i8mS>=9prO=W*H&95J)*Benb@)*u!yRhXy z>gR%ux-~N>z>?sqBPSJ{8<``-`1})gg?YavZ4B*}F_V5iV%Q0E_HVi`pfzAET#Xo3 z|B2P{0Rz*jmvv8)UY13rWAbLa5o=|~w|C-+qQDjzuauw__zSstjLqb2(?|*VJ2u(A zeC=CL6&00{XVs)6+ODbdh(Z^gL1R)`BG{LeM;fYR{tQ|3I*MithcH9lf*3&ayUSG2 zslv>)o{LxKF|-au&R4%1P1@Ee6;BT!N#Q-CqqeSJ9Br8-3(Qa%8`q8tTbucaC2Nfh zwZa}-;MpuH`3umetk<9y9{q}wu+%ujie#Ek?@JM~=0IjhiO124=({RTgY}bzsOLFl z*%R}J&i9gXS(~I~JulsD-t#4`Cc0sWVmPLP(Fgi|!^TaBBOaxtd61ywA1|RZ$D(q8 zLqZ$yyBJ19<|$%@B_&l@AyTj9XqJ)$iI%}iPfUvMVX~86o$Gef>DTwVzj??#6X)>8 zDr{+kXr6sLqI=w1(;*Q!u>^R1-{fkk-z6fv57|(7U)-R&NKUy%_!AEp`79$hUdxIe zzcd!o)F0asRjt3_G(WSr-?m8tBlmi!snu->y{~!sZM#djG}$sHvPWke6~%&$Y%W$_ z%jkV5GDZ=iwT!3TS~FH$wXVW%sO04?WJtife3L@A%u z4OH(G3&<3|LZ{-IC85{U3%MotW1RtQ8u1?kQ^YDS{V^Oe|aIEc^ zw(KXn!t=@0o0sI}<<{MM!JK8iwv?`ihj!MX5xJ<-KUVr0%P3XDfXv#Pvni zLNGXaq(la8Jx{IOpK~tBn4GVnCfzK3pEwH3t}x@+X3QTZ3$118|3PFx7fhJc04|%^ zKc+;wDQ}8=^W4J_p-N|&k(TuxH3Z0O_F06 zF%pnOfF>aExU=JINCQc1Q44+x1CA(9=KMWT5d$UV2mvQsPG=Yfsfdk#QCpnj_j@`RrLf#wO(^j-&yVle5983Myc8mGS zyGQ2|4^|7m0g7Ep_0V@Zy#Sa%TF@7u;EKj#7G&S7tooo_0s~;k4#cVWP;}Bp-B+PI zPeqS_CJV|N2rEp{isKlc%5$XAG*FTmn_P>cq6ZiEwy`q~g(}xbYD!rHk*(IrqTwe)3h%_Pz-||~m6WKUJ6~&Q2sYO0-droB`3=yhBSP@teV$n17~+gi^oVkTZ(cb$E65fXX4c3lg(K{lg*J-t8^5dgGPR`8to4niC& zE(JwndKN1fTD@9p`ivVMp`hD&9(fCwH}>m2sn3~KrvUh1C_Il$_vPE1XP-bW?SfZo zeU?XSV9-42`r_>vcIQ9p5ysA0*HU)M{!f97N~N<^D0<$ceP1R)NO)9J*^ei<`LQ+| z)U`_|sHHMgb$B%d3#r5;F6H8w7nOXQx@HSGGBeGL$SET76i?d2ocr*jHd&^wrdeW7w=p`cQs_*(@QdkBAh{lfm;C>BKKtV;BSRSyg*dHq#%*~&KUWEWP zSOF91#*YBfImQdM`1Vc(Z9Ie4bJNkw_Z-gC0^&<(u~pt>gX|bDolw|-i?%Kd2^_Rp`Y9glaTRrnrMh2 zNfEGBx}adHko>ZZ;7I7Vj7S#ifg1}Q2e)kzGM3^1;ZFf*66RaM{4wf=Rn%c&bjkf4 z1wfewIjbI0sj;p-I{fs<9T2i4o?u`{CX!WaM=;px%Y7pBo17~GbBeg89O$4_@NN6p z=?wLE3Hy2nJ$d#V%g^CsQ4L8Ut@#(gJ?=BL8Now^#WS0pqd)EHGo6oFDjcKgw`o7H zBs9>l{3+lq!$1+uLL_!XhnQfRGZ%JBFEPwj^^vE_^{OzRO(jnIM=JD*mftCWgV zu_i6hTW@G|HiCg>6s)CXWxy|j1U$h~mNJH1nn-*F%JKiFhHH;Y`ikN|A1J0K7^2K3 zkE}pVWxXt&6+%-~e5@LZHN`}ok2dKsQwy0Qf|^<*SK5MUX+9=uOR;4de6(q%wwx6> zE71zG(w6g4+XX-Uckkz%d(ZcN@9%fcALpFgmi%aG3Z=YcQaW4{@7)?OA4)sdB+fd$ z?Vf*BOe;#xP|f_+Mt8_NvQgSvwP+4t$T)4 zc|?0DIn_zN7IwIxyzYiM=u<~(Z&bV$h^7-hsg@7Mk7flw(5!Ea$R^e=w%k|zp?Nb_ z?DNWA=^4UKc>n#aU#t()P}&PRSw3b1Mt!=5F7V>kKdQJ~&Y%swPmT{kDF?J{Q2tbG zXUqM0uisz=mTB!+RG4QmJwvWbJgzS0QV*_*sVpl`N-7)=gLYaqU-MuJP(7dLx7`gZ z-98&Mw0bfq&>qVV>Kh2UGfZiOw>Mu%x0 z{lPJ7!Ysw#3EN7cKMh&&+Dns`0$56W?CWYA82og?{HNABUz{NQ1_N|qM_fbox}4e= zb^H+0-ggQoa9r+;&KAj z6DtdUeVe3z!Qoa`sxHsI?RB=xrOic_5mvK99hSf&d@5lFJ@eYz!BXqkND2aY%Zm_= z{I{{csIDy_8|(3_^vki&Of=7ZpDIszx{R5la|V8Ak5t9p!=?gNCbi_uxA*uCN58{b zc$h_TZvHd9F`)U(6+|h)i!=IH>~C8^>(NXc$@bq}?&-AycI*UJ9!>~>T35UfZw@Cw zGB|}PViQ}?z%WEbDjWBxJhgL=*O|^Ro=p5HT^sKEwz8+_<%FjKrK8Pp5zMD2pNcR0b;2=rFiiIy=Wp|rTc0sOi+Q57iBCgeOo z_%SIE(Mg1J?k?ok2U4tHJ!RIsDmXgENjL67o+_vXox(h#L;&Y6ufy>*%nq5J8i*?Q z0=c33uIpmF9y~LXz!dJ2x-MgITWK^ky}dn!yig}x7LOQ%&_L4zqeq~`iPjSopCX-J znFtGW8-`WW)A0-Cdfm_hnNYN}2N2$Sl?~dId5cycs^|wqJ_21PF6Vwcon@!X6#CS&!JQm0j$k~OIO9Xuli@kt{SQxgpI6(tQ6XYbAX0)kQnJ^uspJBD zLa`F&u}sq&akLa3GpaH$>2?5Q`T=5I3#}xTlN5>^4j~zn`zEZ7AXqo=@mBHvMwI5< zCfUWNgOjn&7_hZ}3aOOA6g3{Dmqg|Q-UsBwE(NL0Ti~ifL~)?#>5aD0n#oeb6%&2* zS9KNxDO6eg??9)81Fn4nWJZs9*LTjXw3{`HBOX%C(VQc mh=)=Us!U_bh{cDmc}7rC6fL0p9D}HT{Uw0Lpf*sV`TqlYEG8!UM8*yp8+je`vYcep>J(g`tA_s`O^k;cm95?IQjC;cG-tg?7{`ndx24^fu{we+ z(P&11_q|ayL{-xFxI^&Ix?i{8?atbbr)B+6cehp`Ip^+uT7V#D_if(Ji)A3C?7Kbx z|MUMn@a};%uIs>V~o zcbj#t3Pc!d7&OWMoEH=X``-CskBxAaWTbo9EzWrLdO;S$2&Kq#KaBWK81&Ez8U!X+jJVW9o0BiZJn{>DKkl}Zx%H6AC%Pw@9R>SG6! z2UY_0x_SHWCmx3``SF0A`^@VMI|Iza!0&B~Xl~?*c;KWXe5ns#X+DTY{%7s~dA5b% z&xa2j(LZOa%gPY0#lzFOLKJ4ksU{`nnXpN)i5841nmR_Okf#wyXVtQ-t3YEyb5=DrSw7f_GjDe zw>%-pAmLlLwllOS5spx#@|;NBh>!RQusX146mT$OXBazg1~w0lmp*$V9~&rB%%~?K zn(eZmc5^^Kh~Z!`*T2g81yz!}n(nSzYa`W>c&)}uRzR>ds1_NTQ*b>Zd&mxdYX%%?ET zR^(oPM)M_-4x#3%R7 zRQFumLYvb8b+M{aOPak9TI^^AmTr3`;UxY7V+%?6+8-MT|CuO|zrLQsNqO;*8jbOjH1DYi;HZ);sBZi+IXE0(6v3>95`0$^tGNMXC z7b`Ykd4c^FwNJssAHVj)e33s{D*l(~egpJruw$WT2#F&7s`=sF#kG}F<0eq5T#-U; zG>UEHQ1%;BG67}~)nmV_Tak_3i1tcgMX7ny6#$^UN*exMVvj3nr=uuSO6ikm#^*gM zxc9LA#>ylt4zk&!g2J1$vwHC|JBrKuSa%&6EVKkr??4Yx2+^7T6-6c}DPW4xjQYb; zV6uNP2&RzuV%K^19MxNM-}A+wey8jHW$Y5xUFarw90;m` z5628{G5rr?FxM=HjDult>a&th6&by1aAh~4ljykCkT_S@qM>;gIB5W@{kP^c#E=r3 zz(6cdIGN6Z#xt&=EZfT-WvVJ7sXfw8!i7H5tobZXtHC&gcf|cFh(__nFtmcID=6_Q z;UFq6fOK9=d_fqRX(ijKtw2cxW#~3iz=(M0mU3Vv<-KltC?t0%z6m#eCxiMP_V$$7+L;BF*es!%Bo z-Pvh0i*Sy`g&+Tw0B=o;m>MoJ zG;@m=le`MO>pbdvx#{b-bcM`^*W5yzTAi;XxItF=9XnNyB_XnS?f(VOT^WnMU>v zUgYVjSDKX1b$Bw1M?bulL%+lN+9(kCX0(%ZwgeaT*_I4DzH){*OFtWzvg)87 zr@Cq)$-UP_%ZCa(!#s;|(toLnY6o04W{SOgPic1%m?V=bxA+iD#4k}4ZL~A8On(FS zj1FG@&us{T+|l2tm>p@7safpDB*v9BZ^a*nLDJMi*eF}J{XJA9;kZo_6Y?EI@{#Ly zPI>ql-Ya7?4jtoItOvA$iL3?w1;1Oh43;55SVILWZB94zy!VhrZ)t%%LC3dVs0BR+1D`?Dl;|(X|5GgGDc)T{u zD5p!f#%GzY{mx(eYeY5PL}&=Bm$3r(N30US8{I3@9HYHLV5;hHO?@m*WSw@;t!MDt zuV(~a`B6ju(Iu-)WI`=b_98$aHj-(`2z|O6Q)bkjn7dBd zrhq%LiCE|FxMZy68RKVKb%iX`Js`}!zyvmuwkKQ?Uy$bYlJw=Pq1qB<4MJ1G#+QqV z+UlIQr`-B-=zdp@r~AJ<;io?(g_Pmwy&4NG;=ka=(rX#TuD|I2?lw<4d( z>iz>)E-lAz5rsucAM-44}*eU&)4vi^0(_v?sJ>q*E`l@{1?X;8!c(dB-AG<(yLl_ zDFY@RiwVYhMgKpd$`{tksAsLm1s4n5RSB_Kb{zuPfzw_o5B` zFqjQYw1o1`8BD0rV$=N+pQ9~gH;X=lo(2zI-^=P`_)ioeyHUsr$=f$NHdTp}!PC=I zdQld`=ei}Z(Ikla;>`r2SMkNz|AX}!W-vtk$c#+av}`K#ZT)FPGOzB&_jb+JgZt@c zHzS5nwa-dj)fk58eTl3BC7qt|F>*+#LLXdarpf@~xKO%Gg#rT?9hZtxf+$<%Lt?($mnIs5Xk+FiA+ZKL#RWdhQd8zAUF=kqdZc)$4dB z50&>F_Y$$-%Cwb*pzb&TM;KXim3}G)sMEcbsA;Tk`*6`NiQEhaq_C>%D9r=BGW)z$ zz^Y4Vo%k_@kxocvP|Vrzi2J(EY5i6?{_}6p&QN{MeUL`G`9W)?}n7G~^NlxRt4Kcs{LhXD6p>ST8UU$#Mjq+i2ZMDcbqQ%Hq&X4k9T zcFT2;%g5$fsEOCdvzeTFu-jr%gm6jKsWoPuFagIFTVL+$p%$>$e$T^BkI|UD*Xmzb zIn4#9`Q=-=9fd>9o#W*eR&BV1{Ze)uMVX+UIzU6wTm;fdqwod-#^<*i}cpmwJKk| zQWtO#+dO0R3pG>LEy*S|lrAb9^BYI0uKCSA0og*e)%AdZz%f*HA$8t7Qu8XX@gl*% zYYV#Faa`VaQ;V%j@B#K6FFA6J?Zw-G_&dK=3X$-SI^|Q5(|4jx{4A?p-`WcTAFb(1 z^0MrctYxM$jc7h6fb=_HxdF?xivQa^|VVWM& z_%&`5lPf25uX~qkoQ<0$-OR;>otcgflf{GhjP*TCB?&=UxoU#pMX<0bp&shkW%guv zzON%z7$m>zFE<<zazJ!w6? zSEd#jTu8+bRFa&6(^6A^zr6TkHYkvN3j)9p7kSm^Yqdvw_YN5jormN=4c-DSIlpc! z!5hu^8(fXOK+ath$KHI05ACCxKVPcY{@voLpnqp()~Q5Jy}sd9bj2HbOd)aRemtpP z7d+;^9~br3Q4-G~h(d>r#?}F48TV}e<}nvB`-5aiiAE8Q$sJ|b#x^vE1BMdM$7i?M z)A0HzZz0J0a&plzjvwY=RAm(~2>JiqBvJZKX9)rWIvw3FD0d$bbhe1aKQ@Lw) z4QrMnRR{8Y@%AcgsTq^^%l18PAd%%usyMhL8&g$SDXpqvpn&~i{1w6Vl8pC(g^8{M zWf)aP5^KY%KL1#O&%vG7>kLK@ph>(UKS28)9GGPHRSLu{1QFUV8t}2Dj4VWc!Ylsc zT`KF8bq{a_+Ual$e;0E9h#jJ|;b&E^V5+d~6*a2bE9V>wvYhRXspSWr{SJ882BT{e zqLsKOkeJ!5len>TU8HB6RXm4Uv-8-_l&w~u9`QB3;TIuSuS!&bl$Z$2h)2Xgc{92E zI4Jl`Y+tC2A#RNxMvktgQIPQ{+W}h0=YBtQ;>R!AhF=A=O=5bA`QA7QJ@y5)ocfqC zIjnZPz8d8@FT^dYm?p4^iQmg%OK!BtqSq#WKv04gv~}2v%by5&*$(Nour+`D(V%TI$3{Jc%!)Gv15|uv!M1~ zuHi485>}FH$5P)OF7t8XBAT=w^H*hU+DdU+k=kGxQk|*D*N80?@rZ8v?;DIOPU}q$ zuWd~?=Wc!ZP#nMWlks|%j7K8pxPSaJ!jP7MvjlOB&+HX$vMMeQb$NdH1O)e?st#ml z+rqd1hFH5VcfFu{3Nm|6mZ5RlHoO4omFkIEkMFuf>%kEK8P*W-Nbqv4cvl^5ycbdQ z6b8ivgX)v;ALMWMEl=%6z84*Ry?(-o%UY8aRaI+!-Y3z}>Ad#8&okpJ|0Tq}0D zrncsOyd)C|5~I`)bQX4VIYqy2V$)M%D9rtd0Ws0T#y&y(kQY6CP zpGn<+e-q1F66k)SHsl01FuA}sb4(vW+N6LRyfy(Ed}sITV$os>?@STMuu= zLQFvdn>XNBa!17@T0cVOd#dWOoBzUWoVmGSyF@B()!v1l8ZM)e%?*;}dl~MzEO%{_y9~ z(1%2?Yngk4WKTD67{AhH{ZG#UDuUUjIUQ)B~i z6e+lMSmT`Sy}w)cAc3wFw^Xl}y1B4cFeeo$`TS^e++a7mPL4}+R4^h4;~PWDqT-d4 zF=Wn{|JwQPnZ~uznZJO4dfz@Qh)(ZMoi=O90yyK_3{Ud`W_9b zwr(0kr|a#D0MuKWPK7zRI%kcq+?BS7uB$3O*H4&U?LHKzlv3O($Iq`UunL^mbGVdnTQxnxm{^!&d>) zPqiC<&d|5)GO6ZXmMm6YQC5Kp=?A4-(Y#h;PO*>8!D@LH1_oOM(qF>5lgaqr`gr{5lN zbkZXRhtXx99WO8n-uBmJFKcwVUCQwpR!ySB;}$RhfXr`P#=#VRGiZl7N{tkfwG8^h ziVZG|3PPWHE(1RkT^r1K+e-)TY?nTHy+I~$Fi5%2v~+edb#B!42#iE-elrFhYsKT2 ztMlMQ#7j!8RhJ7(9%rN)%))!E7wFiF%)x zUc%N=RJtr{pMcT5$x_7twEC%bjnC`&ee*gfp{ZvFI?Ig6krzmibvjQ zmhoQsnCqAekQ~+2)UX-(+~pNi+1X}5_xfKW^vH0zbHFQ2QZesVIxplbfOIZ!%N@|& zJQyG%?3}!E{|0qao3{>TRXTmSxwvfLB=&w`cX@$*<=bLb!sGM;1@22kjUfLHRx&~X zBO9&GztplcwE zCPgK`ij|Ly0>(9zr;608Q+g;Q zs#EEZPf}e`^QfoIMdWohTAA0M5PS8MKAn@|=|{V-x7hr~42DOb`d})Yi;$6<7~zN$ zfsJ007%hNj*}4NR)!NRpk6JNjR{vfnDL1j!#o|R6wJySP^l+2RSvjbzE@Y|lS^L9Z z+O-z!>tyDjn&5xQxH9Sr1Gh^M|K4E(Mn#3khLx%?K;Goicax__QT=TtQC zr%A}FI&$qnz`-+-{tjHi#>K%tCgn#S&O3m2u*~YJzktNFcm&lFLnx>V0 z4BUG8eU@S9bKF@&T50`~@8pW0B=ngjA5`+rAhKcR{;K6pOvvqa)U|1HR9t@eHp7O* z3r_4*#3T?v+104ls;6T)P4!4$@n{zkVkBkW8Vq0Qxu3eWx#mQ-pmess;l!#u$JyVJ zPBm%`I@Rkoos>-y{eGw)^yullT-Z#8658Eo`$;4q!5cOFKP6l;99 z>`K8i%_=u=P-jgY>egr25Xv8fMJ;oKxK?{gkqqhi-pz4-bM%=UyjEGO7TpJJ^m^*g zNA<7{e&;3kF!(n4%AuIxEUM&;5OhG%onI!-oAdoym$tnd{(ICWpEL2IJ?T?2z4?pG zXm@7A{DIhBOwaA7ke}B55lcY@(KnU(vkKTLiy>5o7`7}=J{EzE))@{pOiea?ja_A) z8=y=7BWcL4^YD%z&Et&WPptu3z6Sg&xAsyaW zxF%tMP+V?x-n&xzoCtI>U7nvn&|H?L(shTS+i56hOUIzxF*z+)*Xa>mwrK!P0x&L{ zT=G}Rn!+hki96Rrj7jW+e6;L2aO+adshgV>K0b+;;gCw*NSKs7_Q9xGp0)S%ZYF`+ zMLW4SAqv>*DY^(UJZ7jUFoNKL@xPN{ybM%yT#Eq@6;rx7Y}OyHW12rKuh4U;4aI82aWhj@W=DQ0HgxQlAs73mo+*B!&@Mzd~Ja<*PJ#q5fhDH~bP~pUY%REf55k2vn+?o|4oomJ>1YS=b z#>n`tt3dim?0EzR^_}<6kW1gY=>$!?-3zbWT4kBlp`fj;4wHmbp>5`JwvMw3Q21heK}k~<<;G5ovs3c=f0bcW3e4v&}u68uY$V24y%5+ z3NGaviolTGb=JO_XW^+OFq2KU5S$~#p`AheQHwAWr95jMr&vB(r#ux`Db79`87`Jo zRS$YAb6q}L+sXHSxkd~XD{RrHyFa_F7lrK7icXEWu5J&ZuE8BC^Oh$U3C1N8pvZY7 z9`8kNDq`-w(I2oDd-S>ZZJ{EoY?PFE&3ZZlYub2Lj(HZs0H@>dq4=Jz+8J*Es zr1x4Jr;Qc33*AA(sMYQ~DUZvU@bG&jK>x>i&KLW32NSP)3lZ#@`tl8g@EvW@RY7*UznRXQdNOt5JGJ)ao}JpjniI@d=Zc@tcX} zS(PmR&QGe!;Rj}b-*TA;%h+w0J*-_t0r>=@K=N5tro~rOcznX&9|w&47$+uGa#4@$ z)Uii&a!z!N+0%B!qh<5 zy6>DGD}U0~kKX0d?im^BW#v0B{c~?2fhY_u*N-n{(GSoVwkaCv& z^i-6?(sg#ClZsHs`ZuJRVDAP$%R1NuS1W8}E>}`htGDv@n3KjxYrS{U@I?KRQLEPI z>KL^?z;i47hJ$(Dfkdldv915?xlF0SuxPXgW;!xVsEwH|>v0*kx&I&(J{;Rr3J0IC z^p1&b8$0G3N6totYaec>NVV-OUqLM4jfRdLa9#@AwYI;=iNcS6pN=T%ZPy+!Ve0>K#(x&K|Y= zj#7l3C2D#>!1b1ow&sR8;c=Dx*r&jkLQzpe{Zzjahw-)~XKotvSOB=aprJ0Vl2L;0BxHaPrOb zWg930)=CKDDs5p7N7Z96Z%Zp-7#Vs1-+~jvL9a*MrD-#!92>nhM)kdT+Pxmu{?_!K zpA~Q{OqC(h$OV~9c9tF*K3nw(0`3Y7D-VnBWXpfIl()A$JZ2J?8#ICOdblX+8jiK) z5qP$`;VZ?ROCi3j)gEA2)*D5COWnjSDZ6XB!bz($NPdJ)(KgJmpkF{(gY(XzLMc*4 zV!~2)+FAFC+icQX-;uA7S6z*(l^y@~B83_y7YMKDRlS$OrV2hUG#-fi^dxP4y=S3O zo_|yQdUN9{r&?RL26tyu54_s3bL@e}IJew-w#nonfO7!tE0*MbDc)2N?kexQ%kA6k zLCZ_hml`!MI5dYZ`ldsJBcM-!_wg8)w85}Y7MW3g<43J)fW71ZNQlti8s3cehwI%S!lBlAGP2d03mYs#QC~yPfA<^r1l0b5;SZTY?OsWwz9s9| zcM4pF@6L2nd>l2?y|y8nS~d1+vORA;Tfz^0{IAU#yPJ+1$?XMtIT(n?sh>Iwe`tPu zAEEv|si-dYZ|d@0Z0OoDOs3<&Dxezo)sg8+q++0|z8y~vw&2fSsq}tzg9iA_$(Aef zwYRl-+A$me*KU^F@M@hAC(HBBUVC07s7D&gXS&C){jT+5zw=s`WGLu}g}XuxthRm? z*_0G{Ujs(#k_;HTEorf)jw7C&>C!q&GX0a0NL+7mN~aP+Bda{B^xOQGdT_Dov(1#B zZ@Ha%1#gePw}36HeK<9FrkYZ`B5Jt7d5UxWv1obn**qEUsVQ$L^=YG0X4z7_?ao?1X{@ zY}Dop8rCklSSGDWIzwpSQ z$ahLa|CI~$bMNeyHCwYS0D?j0fi9m>n|nDWr0|*7(Cyj#NKX#&dQURe;1(TcEJ69m z2amhmalAs@X;=9vWL>vn*u;~*^3Itq(<8HwoS?JJT60kVPEv6 zV-GP@+{LOaDR7N4SKtoDX%riN$2-;vR8R7B04D_&u#;x8nU8UA`7-d2` zerM+!Rn?_2>ms@~)&;5m7BxW~7qFdLAxBl!Xx?OV=cv;GHKk}ZMKHBV zy3nVX%O;kssw^~Y>ixJ0w71|a*!LYQAJoz3Dc*xo%c)l3i2q@&+e%m0s3)>fi0U?1RL^1x(A(~1n9c<8 z2_~M{>7*^)9rr+$V)YzLUN_i!c;UlbI>NVUjPUa2u%iLeg>dOY z{#gzoG@hR>*M?7Q5*y{(Ws12YFf{iuv~NYfeqKpxF(U%~n-) zp*wDhE@a7_bML*0y!YzSB&q;xB#i-p8m9YMy>zUi)qe?kco+=rx%-*%thC`Z%AWqBzhnMzmH2berwyf#v;skrFWPAQ2M9Sw0M<;%4Lx!4T)_>DB=MY?I1HS9zt zCD5b>-U04DbVSbna;FEHWP_UGQAbkGZzfT;G>(pwl2H!%oc}Ctxib*irfQu{qGVr- zsfyW1<?%Evd>2QV@eN7BbR(4cla-uBqQJEeNpmCe$Qf+>VS+mE=z%m{i zx?(+cW*lU`u+19Y1@3+kA;j~iO8)!7P5NtRJaN*kE9Fy`C(gmKSi zF=kX;dv5~%Uikd#9_k`bPAHE~?`pixc-a7|h>}$v`Ej|~HEHD(>@M#ei1}XeFEu;WG~2sohCxkPG?u-t2z}?;+t7xl3%=v&;|_u)b@rOL6{b% z(MN5kRZB<-uL&iq#-KKRq-JS28D9uKm#l%_oiBKNr+EauNae~gDK8fo72#`q(h8)b zd(?D-w>xJwehZ*BRCZ2|=c6BaNTIM2Wt(`q!G3TP`$?AJ{4>ZJCw)0zTeB8V5VLU$ z=3VTSTHe*gGk*OUs#!(ZEB?H!%j-P%DLv7 zHhL@KANFu#@mXWfZY16z0+vyJ{^aPP2S>B(`RDT0QG}AKq#`xFBEEHfkyCU@3_I_n z&TZbZs^HHV>K>9ZjI5EctYyTHz$LNfdNh#;u|{AZ^pINElvEl>RDGc!3E7wWTR|um z1^?YVrg(7ofSK4yh0M%}*1?ZK=y=~0cq_s>DkH zMo2Wc2XZH?@_|~-Y26os(FizrtlNyb4s?KDd8ODhsPE-(?fn+-{F?MFEOv26CRQfpae;!|;U-5H@la1YT62-Ti3>D58a?mzXWp;> zF&^Q=RGj#Ya)a&z&5f3w#K58Cm9G(ZtzaJJnbc=v9}^$BUY;H@h z<$zWiV#THxBlO<#ph%Z6SjogSG;$GrKAve(`p*v#=FDu(%M2p|nyBU~2Li(556y3r z)8pe#mk4!k$AiJ!l3%v1j(Zmm#AiVVXso^0_ftfW%k~wQtnXt?tb6nI##lc!TWWDV z7^avVE`|P`s?4Lyx|;|S|tCw){Rje*v<-^v6ArMC9^jpS}R> zu)VX?(VK&a3R|G|XKlk|4-w5wczFH~8-nD$eja4{^<_oWjI)ht!+fRYXgTpW}#GB zmw#gkdH&7E|H5~3L)&rcH(55~3_CB%joW(#msh}&hjr|v@%BC+FCA|5z1;)J$wYh? z=})wmjDWWwqUvRJd$mRXK(f?D^B&QI06G=YLF?;2$UmYr6&${J0oP%2>gP=>(=ma@ zj2seZ;<Sc^XI?*7TvWbF^Sj>y2zvWX!$WcX zX>gT$$A1I3t<}<_+nAkghm^xoFz+ZC;Vu!lDx8)>Ld15s2F%rj`r?Q%ZOXD` z78xG@Yc?lES-VSCSTV?j(J4}Q=D_Y@vTpWI9z(oL5<0 zQySl&={dWP(;K&q)ksmLJ{m^+cT+#|gejLgQKtR*W7HwmiheT{s!b7_Le^)=w&OxEf+{P*|GHS*Xlms#~6lb}1;Fuq0{gWY13>&YKoZyby3 zyF+C!Hq%e*x)3kkLvLEIBoMkHiby-cZ z9MnyI6<-;fsKChJwVdX#(d06Ph1jH%Zp*ODmE+;P03r|gyWvwEsG6FanS!{6X^>Il zGKSv4fddyla{ecB>f?EE_pGSvRsTGW8bv`Jbr~6->u;5~ zefvdHccB&FGG89eEl#-B>z&h3UyL5F`#v^o7bk1Em$X!y;!hteW4u!e1RtmM)a(Bg z%CYL$tO-V>4J(!7BIQ+h%RTqYXQepxpi4&@+)6sb6@B1N3Ga4(Z>oWvK zN}z{6>aDi-Z^kpGQL`_{n?|jKelFc2zamanr^H6~U}c%mV*|&K^Xj!bbK(=COL#gn zeY#j}{(3$#E&A5M*5|T+WMLLB^tWHVaTt4#JK$(`*ZQI9Yv6~c3|V98eap%H@F*N9 z-Q^knN_=SIx=BgmVzcyh=8sQ=JtuJ|My%z@*fmZ73dhLAi6{y&+G6CGsaLeJ+Lr$0 z6AZfy(@7e6w2w^FCv)QBPfv%*1#Bo`R3$cxO4tBGtZ})>PSM2X7!-|6w@~WnMBA;k zri{Kv=Vy?7bPP1DYVUBa+Uod1Srob}ojW^8f}`+a0hJypO}4AfxJ0jbrM&Vu#9i7mlu@ zid4FZ2{6LI#J_@g@7WKLz#pZ$p1uea=he0Nlu(OKN4XKTImm_z|e*hbpGeytMkzC0tVkd0zaeFiMw+HJwr$H zs}m5J!Sh)RrzL;(ki@T?{{1=}Gm8T+(;mH?=3gPGTRH!tT0I2<8PLfs!y_ePI1CsyzK>+&)YT@8)q-rk$h zz?j#gC{V94M>m2e^Z4dv%yFn0hLE7aX-;XK0gBSg+pVlJsq1e;pO)AS4s*j)+H$v+;aY4}m6_&u5GhOha7Ld}1jfcy`NiVITZG2T8B z%#8N$XZi3F$W^eB+eCpHOF-KwL|Q<2Np+K8!L1w7s9*_XFTZ)kumWziD=YW5_+=P6 z9FoM0-L1cq0mz>a&=%X%7KUa096%D!w!&FUxIp=KEY6~!y}u-aCeQC6F~IkgBJVU8 z3#GCY#BV!Xh)PiVWW4npy)NY+kP zTdYtK%R3(?yjK26+HQ0uS`0ABBcP75xtY)7HQIsvHTrzrv-AZ+k@wnl7*W<`Yt?=E z=>cjpFqo#HWTd~8CpB(|=iKjFm{~9tP9=W5{Eh#35wALK-?y?%tdf4r?9ivoFmI#Y zDPfcGEpX;9C5GtL-_2ZW7geUZ)9mwR5vb#638Wya+4nA-L1%icG%VVaB6h9hqk;ZP z>0hN6QC48|V_%Bb>2bmrm4@0E*^d}I^}q`^<(F3-MUAd;gnCcJ8Y#6@hjxK`H$QSaH`KcwQkhis8h=2d#Iw`5~WHL7`)1uxopMSdX@ zVv)G_MX<~a4#fw-;|tslI8G0 z=Z9%v%{txomqV=`$W1G+sG-$L9(JWls}dd(WbP0v4c(=%$u#mwtp-6i(?eziQK;i$ zQs>v}8<^K{zTT-2@(~a5&~5?&Mai1V;~O&waD-Cy@O!a>#{$5j-DV}QWg=6+Xzx! z#-r9Pw(eX~GDkPR30cI4^Bfw!E}kLuI{0z2>9Q-xrZBt>wZ7P?@3}v@WP-f~H2;=} z2uB&q59$<;MMu%r(=%cw(zVBcmH(fG9I%k9I~(BHeCL75SkbRo8BT?Mq(Tjlpw)cG z6Y7daOKZ^eEINe;#XHzcbW|FF164}(Jxq`b?Ox;Ea`YmQov^I!>X>S(RIG6uX`6bTxk@; zl4Btb45ngGp=9FuNJbZbXj@ zN?`qSTO`xcv3dt-WCLaKpCpC6{Ebwo0geumYZR#vy zw;QXihOxP#Z5SmO^J?o`2-q{F3M?}9yBGLh)tsFRsxQswWXb=FeTtjP*i?Jlh7p+35o{?o4_ zpY-b`NTPk{baR!S|840()1vl9b`~J$=YAW_(9Q#Eu~OQW!EJudvS$~9p;!N3!jR+IVJ5>eZzlFpGYPeLJhOt6)RS84Oo?&SVF zc}7P?0)ryT8JyV=k98k0jg)pULp>5<Cu zJ>U1B-mJE%gN9@{287t<0d8R(JW_}vEIam;u-C~vA5{4G&Ab%A;@7UFf$b!UKfD~K zvhKL7RVDFk44`p%d9uWLmpFF{e+oUjqBksJa<5!6^cna2)OjGFXuX`8l(9l7g^~AH z+w@z3_CBSglFHLKKGQ@gg1_lqJiVh3yok~4CH|ujka_0rquJA(@t~ndLEu3>EvaP` zff0?48gy}sdn_dny=~Ff6#7jCzBzk({v>E# zVE6v(y&4iFb015e<4HjE>CO@rE+O$jQ3Dqq3A;`+x|(Dxhvxe%pebm{Zuu=W?&(PK ztl7({lSl7n`X+mNZ{*QtdnR#KUFpO4( zyfAw(a+dxU=x)yQ)D!YIT&4HnDkTB^*nN#2i}~oR;0TG3I*G3|8psr;$S{s9N>yYF z;D|WqEN&8X->?PSDHE{J%708*CRdOwco%Y3zOkKlCDwfAF0$`x$a(Qwldsq&W@FA( zdsxyk63wZ#_{)712G37v=zrR@;@D+5_$yQtjT@w-pup7cs3yWLn59THhp4$DjsMt- zp!pp2YzgVn&NuYGxvaP9_GTA(bY5PFPRB{X?yjJs>53F#)5@?85G`tnFdTY+=TLPU z`yJQe#O87zb&g*T8h!u0xh3@cE5f;8pq~+3{k%V}3M6=!{wnlZ zZ6}c6o78k+y*^nWDQs!{zoNb}psl6}7kAeH#hn7dDNvwzDems>(o)6N? z&(ECB$-q*`ZriDu5SdBD!Hui%(nvN5aJvHD3dP;GwBPm|xK6o! z-|dxqahuaxt9m4&ZX7Nu6HQjwjQ4~s`&`bh)ol&_@_k+o7FJHO$jKCMFc-9VqH9z_ zW30*og1UBUQFJtiY6;)+KS85Mvy@3YVLTvmVqy~@=;>lxbZxwIt7b2^*~7k6pSF$Y zt3pw(&zb-G^O39O)QX))PxQ4%pNU>Vz$ohBh?6~Y#hOJCW;y(KZ#)57J$Yl1yyEY^ zrckeC0-|MCoN}}2((bApk&h_-8!Y!wyj=<;NA2kv?cvIpESA-Kt=DQr{I~BoPiOpiOVc)06T`Nnp)d&6Fz&p>sI5P>!)NuLmG_-QJU_aw((aD z9Z9oaLXB(KWXUX@Ln<*SOLN)~k|tYvo!MRPo(DX`L#Dq+y?%k6WVH}MT^*IcJ8h73 zMG4Bra&ZauVZ?QuOL_&?*6$_d@tVuWX2ZF#^2T|=X4Ba9u5>R;B&a6OylxMY$9ODM$&6_Vvo@^ zQ&Vc&A-*L!>{Qk17LEsG|uH_&Jp9erDxR;8oWXg?U6SsKAFpEEkhV!M4V)3nB8 zqAn)J)1*|D*bynXgE280gRzT-M2cr#BAyEWyWMZ0!3J1ib{dCL{%bGCo1M_L81q5J! zSJTR1i7m7%DG_{c(k-3mS6#0=-oU@?d!2Radx@CJ+m}(F57vkMJnz{;l&)24sZNZ@ z+RIuk+3Y1r7)fVTtq%)-Shh1iRY`-C^sbgbYAzbv(x=sazjlWs|NBp+Dm&boVf@wY zxPfvy2JLQk|ZI>RUT;5~TVf!ZC>$=sWP=vRTwIc-@vneP54)7?d*#y@Ko)KUn#Wd_lpN zdhn&_G##`TP=b>oVVdx+{j6@HEJ7y=#SFTy;Pagd@H_v@6@vST`2#&~;^E?FTA}aL z&+^@0+Tq}phuDfG?&W~t6SaI&$7tEX5gO72Jbb5kA)7v8W=4;yTa^48ZRKn%$h>2O zwzuRF3_9y*#P+Vn^YO*owZ)dt^PiY7smP6IcY)1!MoxAcPX}T1j=Xo7zt)`#2;L}4 zvegtP61tcv^;ZbX1gg$J*CF^H$~;Z3Bcni3SbEqW@NuHoYlHP4#X|p#iYtK9!1o>2 zwV@#=J-2lxA8Qn(Ff6GHNob{`aSm7u!^}HLBW!@S=fqrUEUfma#U0g)qS6yk?ptl* zq?M1e)~(|TR;VhHYD&e@2od?i==m#X`z!Nj)8V%+vf|6V=8euIkLVe)?_%lvH@Nam zR!|oXeX`@@xqtjYdqd5#L(J8bf1ScE5iU!{;t;hMu?{cC9T^psV3V1cru;4hM_SWV zgzZLBS0?e5GD@1|t9m?6OW#dGP0B7kOkYy$B1Zvg9xyotS<;f=y~ZzMq`} zJ)crJEV8asn2yb{8G`~~QC-mmHCCMu9jfLb5XaCN7*$k*c{}LL@I2c$T|ll7sTLf= z?wtoAu0j%>XE{VGufihY1c=LXV+sDDQrFgI@+@gBjTBzNGwqA`8iGYg?Hw%Q9g42E z*b674{mO{TxL9qCcjUAweQb-@2uLu&-isFGPc6BAAj0E zk<&_Yn$DgbLYZQcW4g44i?+U~G^ynqlGWWa(bhaHblwPS9M&`~y(u4uu&xgotsKx% ze}KjJoq38LHb*Pd4gWGslz)F^_)%sM889MYBEiO zP6KWomi>`)=$ciF)s7o+a`WC*x5u+}9>%XC9S_%synh`4#J?1m>N?t8S4uSwQ>(OF zELRSA4@cj(nJ-Lrctfv{ziXYY3g3*3M-Vj)Hn1Ai*g|NPkoOZ|Q%`+mhtjac$?IVKWLD`~r6k>vKF zm(u-{MF#r9>7_wO4zCd(vmxwhF+G9^+QrS37znXK7^p{lm&a--o?4tdbDC)v`4TsQ zFhY&TwDwptBy?;OYhu~uIyUktEzf`V0{8Ls_0c>MQk$nak!2eJspl8b9@H$=!TiBI zx*@28IX~k1u_-nblSYBNFjJb<{GdS~1duz!H)W9Z6V{4*lFt3*9@%X{6ZD7*uk%aIBM8A{u3b`?44)W z{YoQS4J z7z^-K-s4shb=ej}#t>{kW&0wG0gmP_&b> zJE(7hhK+5uaouFjwar_^T%Ytgp5_{8^=bt^`G=6xs=(i}&TMI@2y3P|@n(-m+^#xb zadwgV78I7KG}C5=-;-JSTNgX(;pFPB1N)4eoI>rqrUPMEPOaCgSw?vxk9s=eOHwV=j&j`x%hZyno(=XPe6~#}&rEFVN<8(%cGL;HTf##;QGr;tM1;c9Xvdun@n0P* z`}un9uDUJFF~*T{ZUdU%l3J}kNl_ZPjpsO3=+{T)Mi>il%Na|sY`->qlOxlN4nfv&ba(RUJ8NC3UM zM_hkdLjls}T<>IqBy=x3lkKuKfa=z4JI6v0H;6yTNkE8zVWzfoxPpO_PC&pBsXl6} z_BGjQUr`>f^*Pm_(rpTs7Mq!A=+GXU^JJmLVR~~%j>7jM^i=Dqy)}&2#@;%f?VZN2 zVhfv^(4Wh2WRAfffFE*lm|TMw7wvqMw<4v%DTV13n!L##4xZGUrW<|Ilv6tsdX<7j z-nZ=DJu4f1Px`kaS7X1Rv`S~*OP7+3pd3IQR;W2zq+|ywUS()shZXi{KA08xl>6xo z^$m$1Qi4UsKGabft4*Xk#B)G%G#3c}APIaJrNjo*POkf+qhI7aTJKd>M5R5||CxE~ zcn3bX0vFs1H<}(=Hh@36vvSODe%IAM^HgCQz@+{ZBE5)1!)R>iY&b8@&3kZ)oJt_~ zR?U)phTtj3^1sPI2lg`7UBmIz9K4l7mz)Ju>Cf$nLZ$iaP);6^hCAjaAD0g;+(PHs5BKZouN?)cyawhAq)_+w%)KF zhCndQ*J7I{3A5K+jh0jmZIa=A{f$W)u!{*wLMdm&t>?7zGPyX^eQDHP?ER?fG&D`f z;UqB{O8~6yOr(P{AgGMlh&XN$1vK@Dd1I_`skNbGM|~A>!Co}Ctb8O#4iPZyJe;cH zRC^pl7$E1AiW!$>Iq5?WoS94A-LYAEXyIU)>(#=9I3K3}1-?7yZi%eCsBJn`-7kwZ0TQwzFu2W1pogk53{N4$9RrVSSK(3nhM`psJSYr>e@rxKK`XMq!U;y=SB@i+lwk%b9n> zvC|wrCnyxZZ9(%m87Vvt?m;@MWwJv>nk5Y3VnPm4?E{)T>|m{_ zq-&dlPMe<|iQU3tl7TQ6Bn4b0qKl{Rf)sdE zdH%@QZ8w!TD^A0Hq8A~eDQ$`)cG#ydBBwW8l&>8rG(3{T;V_;&VUhuVeV~CP$Md>6 z(dL`jm%}=!7L(e<<5z`#r{5_z>ALN)q8R?w+I$*lMUiuP9B0=zy?LD7EjJEAbNCj0 zFPM)xZ}5JXvifXz*oE6r}@M8!yOyiKKQK!f6rakT{!G0TWF>NPvbY;I7xI%2sEdx7>3( zYmL?nX}L;PwJLN(Pt$AsTN&RRH|h}3+rPu-Zs(JOJe%D_nkx4&4q!9a`d2rfMP=bw zqepFRURf=x1sz-q$FBo0LF9NLqvSK&m6M%*(5!<|4xkR9FsW6JlC4Wuzr1D%P{gVC z>>vCKWWTDLj>5(V^4jygEM^q#FmC7d5Bae%*(ciKFCvB1%whK#W(2r81Q@Jny27wq zopw>$ac~EUHSS~>=U+a`{wXo@rv7AOY6;UDB+AMWX>JtDj8aK&Ty8?(Se`gN5lNCPFjE@jiOVI{0<$m)@5+Vp;>hesHz-HdoeIsh)k9$8q8_u|preokw1?GX|g{7B2*T1ZP}Ubx8C;1H=xTwc@Ov1VfAkMOJ!jelDchsGErIxz>cG=lb}X)y_| z8O@ak!Q$6Xq}Ng!b3_7pHsA99cWErCPB;OuQCSJV5=J zD5Z{Jp7D7`n-2@56_1u+CT5YU72K*X4V_${0!&1!S<9SLe&K>ob1G@XTWqdezbR0G za7=9pqRw=YesmQoHb1c#&}Exxur4I<7|}#OU2IWELO6 zaRg1V7>=)}@+mWxN^Oa73{#$_WW!3(SfiSj6f zg$_pYv#qwgFReA3^s-vPrNO{eZ79(WUt~Arne{{WG1igh@bZ1nh^(d<$Rp123M+wj z#4Co7mJxDzm{#RR7kyxh+)T5J#d03Vd(@~v!0}>#^S-%TkTzP9641-pZy>+BI@Nj% zFDH4?-O3>AcgKAA`1MDEhrQKA?Y&b+7OI<|2UvE{F8|54XbLZhrW(ltEwE%O%EwMXWk0EY&fI5AKRvmf9`Ol{&s(#`$Ub9F-&}zuPAccF!t- z@ZZT&ls1t^?$P2yd;>i!k^|<17@-ag2c!Wvb@ZwQ=0&w5GQf$L0+xekXDks$#GD>J z!(E{AGn-g3Ygx^>UL%|AEmvjna*ebKP0hflv`0tFO(0S@no4jC3Z-mthWYhqnkXY$ z<+Qj0<|8lNm)?23M1Gtq*6L0u-L#SaJOdQ!XG&vSx-ZU(UYHm($P9AT~Uyy*I_MWo0Hc zrgQWU0|z<|$&KLGIn5;Q5ACWP*z544A|YgAw325L{Tg#WmTUB7P@7&W%#DL7dlCo= zKBjL=3_4*`K;XqS!1}K5Az(2L*b9f z_1r-rKAHd*V2`~lu?NRwHL|VwFRTt0eYVi`)MgNXb*ASBR82ev(0L2tv1g{^Kcxz) zxI0iLwpEUxD^8jsy4IvI@229aHp$><5718s4BYjKzvOjXKBg=+pWKbbw|Sqglu4=( z%xN4V)R58-VN(U#)dNFgLKA@u2M6Ms;p>i4KT6%p2Don0!nLP?>^2;r z4Y2HRWeNJY-*bAa2!t|`PC0WUG`bvlx+N8lEFc&+`Ji6#BP80iz;AiX7`OVmQqLS+lB~sqcCkjO;Krb2;BO_8F(f%|_oL@X^(eCF zDh$jMK1US6^W1wi$YlPp0X5eE7RTn^F-8-)k@Y_4p41F9q7&29bD{dD>1Lf$#<-)# ze#Tc^b?R*``jzz2C!}p(2KfBsUh30PF|W4c_5^YFbJlySCd@^_5>2iUA%w~1 zV%&EkF2WMGsh+mpe$wso2xC|o!wk^O*C=gpQ+gm1iW}pVDn}R^82BPxPDV-&KVxQW zd%2`%Md@?b{X62#<72qHum5*`lWNEzmV8iZ20mw~LajnTclznXkfx~ZjPxRMnhi`Q zVlOxLS!3TQp|+wuF}tMoFPL~tZ=tj{dD}^WE`Np+T6hO-9xiN58z`d?{NCYr#~!EO zd55uo*JHK2-+9oRa=e*`9?U~A@~qnBaDBp(`9pu)Lu<9BIV{`!%*eo>B1N5otz#m) zEOA8icC*ctepkk)IZC3Q*KrO{o2y-~)8l{Mcm?Z+C9jMBjJ@c6-;l&2Q=pXO6fP|< zKX6!FFH4qdm>08onj04mH(85b=mw1HVjj`_7UM+g&(`uW_;c`0$iq#s@kq`4>$zX) zT520RM_^1CjFBs7@ zx<;u142EvmL~bko4gX|px?$ZafVD;BV@gC+Oc115EN<23@T6yBqpz*epeZS7Hb>8? zt4qt3!|J}Ze0bjMkMP7EoYwtq7{qJbR6#?4oPraGjtDjB8?vvr4=d{t^Od4_`OV$w z`=97o%u6FjowoDwDH!F(kcCyx^S+Slz4z@-vg8FcSboBQgt|a%o$}r=)J~mxT-&6B4W$+dbLac5kjvuhhYxUuM(VFiYMw52>c28MDD-19k3WYk^O4nt3Cgw%@ zj#S~uz-#Kx9hr5gNu2li-HGl~p|l>y*K4`O1X?g0Y$&;$#oT^37j4+Emlqi?s6U}o?X zv3!#Wgfe(I3+;t#G{h@MZViI9zE=ptVAR5zF2>OQR3}p-lNDf6B5dn0Ph1#bRNODT z*uAITGK{zP>uy1Lhi}p<1w!wDK!$^JgzXN3kB^QzPVV)rWbNfD_VG+wReTg#$kp0I zqYEzS*tc4FYP^;Eo9h)KtwkF=lkI&dK8(XxO5NmG8YtAY>IbFC1?1f8!*CRz;LO@V zu#Z9{6Qy-9eBwu)ex+LEraUmTfAx2Fe~o3Ew{$;Uk3AC##<a#nuhqjw7iT(KP+JWAWlH54T^?4h`cTzFuxG!qRVQ2B;wR@xe9b>VQ*S%d6YY@ z|E0U#BaL4)HR*C(*wMxM*4D5L{meGKAv|(S`Pl9HH)hJn_cFgK)tmY;<>>od7PJcC zgb4098;$IicQ%|A?Vqk%ziV0Vws+<4=H^kH>iZn~8{VBjc6U<*jiP2}>yV8+cdK5! zK0m5qo;dQznsKWaQUvd^`Fh_Os0-2z6YXaR?1prg``1O9!5GHs z8C7>v9kOy#`)s!7ILJPQjq()Ie(%vu%yUT4!AoSJk$%6bF3-dcEP#a5{}kGse1_ai zWBc80`BywPDEnS5Ee#F}4BC8CkAAy~^qTfwk%$>&RjCGa`_5q34a(B4imM8M`bZzIaI%s#+=kLFt<^SmZdN(rw*$J+H&GuXr-~I7N zN9bcf3f2%?#vZ@A3%ikyK5JmMm(mnKMt;tvP-C!8zpfRss8wpW1OqT911IxBOl=sj zgq@r)isKI#4+KPs?^_L}+PkfE(LS*EqJU&EZMzy=ZhFyyZ;mM6yqrP_()XfAUz=<< z6xF%ux}KfZo_E(?`$S)FcW>}1oj1boWxd73k9d&P-g0o;)nBmmyI7dzp=I!=*EqL$ zrK=yaBeWaOac&icQ)TghA{_c9O?~T3JKo)X2|#t(9#kGR%=?m<4xpr}Xi7-dk|jVI zT-qCaO#6xP8#?UOZx8+N_vWKpc7|WmzOFetLJB?48DHJ@ZkA3(h{+r6ChB~8GxHCWgEbpY2+YVHeb9Dj|2 zj~EptMWhu%jfqm!X?U}`HR$kJb43@6;oUX~Fb?6@_bS;C+=< zW4A)Ng=+!pyqP{4Lv5p??(vC=)upMvfG*doe*bgrmKsL&6|WS`p_;wh&ttb}**B67 z!`HIpGqfyPndU3br87_g-UJZCAT45X;=&eXX(C1h3p`~X*5Qzd@>> zSy9i_l{J#ls5@s9qWm6*?@F1Ny1*~o74evwv7=?_yVCC8#>4|#g#?|w%OOcxjw3NQ z-;)yfz0qn*K5!32i3cs$WFhqm_)?H?%r3_yhCRN=EG4bR?^f7X%h>Kh4=g#_iwYXH zNXs8xFRyn}t$xm)0 zu;BU*j7zBD;Gwu`7KjM8YZ%r>%_f1Z`;kaOhz9z&V)5~u#^RXQ(|6Y55Au0t<5k1h zfp@05Z6BZch(1r-h5FYp0)?%;Em0!{fK!tmQ-*qO9P=j2wW2OeSj1s7L z$em}*W-<&`{U0UW8-sE;Ae~v}gzi0Jf7W??h^%x-cmz9J90Yd`*+o z<)f`QfkdAOXGuSa(q9NkwCvTJcV~N#fm~4g;%Af8E!gCn&$rdRl?c8@*rnRAgEfmq z-SHZNeN1XiGxN$XKlS;|#6dIDAmNzt$)&i}lvdX8PJXkhyB5b3z7dD(rYgGP1j?44 z*C?%~-nw^rc#(c-9Lj!2jlV$Gy>Xt&#P*0t9E}-X8vQM~=B&AT?@L;k4wfeqGScKh z>i+ZG&Bf-AejByn%<8L4o~Hb7M^ySodkS^wX>Mzigw8EkKwV33XKL|@gM%G@bMLLb zy6F>1hnQbeg4GRP`L1@hL%vK6B54-5;{cc4E*u z!H&!EP1jo5%8h~GiBtBKmlof<{#5x+4%Xq-gu(g{oCiDR;cbQYQhD;LfV&LNG<6fF zL`zTn$oup*7>k7&gJ`h1Twx0+$zp+ARyw-e!aT3Ye6vO&0KWZO%{=-c6u0ZEStYBk z^S-UB2*arr9tIOo?i4d!yRovUJq!)kcl~ylYTQPwyd7!Q&Di=o!v3hSvG<`|SF(A( zdP*F&`RE`N1l41(($e?IKQSs{(hT-YIu3D&0KeWD9Cfl_$G-e22l_kJfrXO*JeF?w zh#bMn>g`QK-802kn!g_t&IhG7ic6#DXE#BT+?kLo43u`&g9j2w)mb~Gqpo>9nmAj5t)j|9N}EF@E7goaB=zuRw1&ScOdiJ9k-7F}xk zC+BFcDKXXLNKSDsni)HqmETp#)Pq*E=Q#^fy4x5QLi6&W#E!2t28L~NuJ=jH5x@Uw zk5lBG zrI;+71lGnotaeqHo@b9Ue}k#wZ9okWi_zRmatdpIZnjbg54X#n9^)M@4OSwYa>3OP zn2-D|36(W7k{|g#ZMEP9c@k$>^DT!Ou4^2^e$oS;x13Xfg&@XGxeR5lTRuMkE$>QQ z`#n#d0DXqqwvAU{L~HY1u~-+Uwa(<{W(1t%KL-tDxmSIS6Ujuf3||7KT?ZKqwE+;~ zpnd#XsdW<0DniUcjokXfU?JXO%K8(&4e^j~fhTL8A{a~0ojbvM1uoSp4jPO6-RNhf z^)#>Fr$xL&%MT{!Myge!u(!|IJftz@l)J9B?7R;)arBHO-pn;G5 z-~xZRQRD@Sw)4z&8iaP+-|oVXX6iB3kbyxhhb=JGXT~O&?SNot zYnC(5LX&0YDuEJ32=i3y7WC~f94NLFU`=wNx_eSs!)wR1btobN<`F`;0YP&+2ToxH z$g<#Wsv%qutfr%(sMflMjkq-;kRB@HQ6PW{(Sw)VkTsOp>@OIE{J#n~Iayl7=CI2H z(hQgfqHm0$KR2WTE~0v&3wSC5aOD9y4HkQWN?v>GVBa_t@DRxde+%xjG8+*3g1ew1ogLRda^;x)$Eh-6%>bfVFbB zd$jPzCi@WQzk1->%R{021hD|X`i`GyPRM_syAeI+N}p@bpsl0ez&FEG3HI0(^10B8 zO`-9o=P{}*t>#zn1(Gs!HQseqU>Svm$I2F&f{}e1|(>9O`*V1qSLLrCYgc;5K zjPQT!JDkA(pGMU%z_fM505KdYBx_z`ea*gqVnsV!*tua21#Xoi{DqYl+Z09<8ZYp_ z7nPxuS@OTXFJg0|Xll`JK+DHJrUDS01yKKM3~J}Kc>i?>3~@KqS^$1$INPz6hHV=g zxs}J;ghlHbZknFjP+jH9OHu*(PUFG<4{x3SSIIid+)L}0!G8b}ukZX{`4QS47q$O~ z6bejQ%Kvo1fC0YU=m$WeKPa22&$^lNF_1H)#ThhZg#AUNG>P8QuF%Kzd4cbL*iXT= zZ<;Nvb0PfgTRlRsezB}H=DWs}GgZ9i9O~i(v^1U%iH(a`(8YhZmktZXwiv2F{)^5R z({Q_#yb@;LYPMD0PtcW9S;Fnh8CRS0z78pjJrO7&I4zj;7jjtzI~4hEI#kkFMsT*; zqS=9eRaC|`Rlwz~rc~^S;)IS56jEy(eEirrbeO*np}pV?`xiH;6+$iVuPyLWnEpBw z5y1Xi7u7tK^dGg)|GHrtYA}^Zy??uFNE0;Y=|Eck@0AZ@q}DwLf6W#80(-!}=Z2ug z_)c2`-+%Z8*&=c3xw6CQ|7#{)BO+*CmlES2gu0?&`%y=1=YQ=2O!-pHb=>n{|90s& z-VbBDq^AFbREDRvtg}@~A^BJ4Z!3RA8%sk}Z93Z&Tr+fZKn*nj{qN&iN>evQCB%QCz?wonp#Lib2QCjH z>i%o42p|ibx Date: Wed, 30 Sep 2020 15:14:49 -0300 Subject: [PATCH 008/350] Allow the user to change the BackupTransport Set the following config overlays to activate this feature: * config_backup_settings_intent to settings://com.android.settings.backup.transport * config_backup_settings_label to some user-facing label e.g. Change backup provider * config_ignored_backup_transports to hide transports from the list Co-authored-by: Michael Bestas Change-Id: I080d96e2c34045a0e61f3fa1b839f463550f2028 --- AndroidManifest.xml | 12 ++ res/values/cm_strings.xml | 21 ++++ res/values/lineage_config.xml | 21 ++++ res/xml/backup_transport_settings.xml | 24 ++++ .../backup/BackupSettingsFragment.java | 7 ++ .../BackupSettingsPreferenceController.java | 14 +-- .../settings/backup/transport/Transport.java | 29 +++++ .../backup/transport/TransportActivity.java | 39 ++++++ .../backup/transport/TransportFragment.java | 69 +++++++++++ .../backup/transport/TransportHelper.java | 116 ++++++++++++++++++ .../TransportPreferenceController.java | 74 +++++++++++ 11 files changed, 419 insertions(+), 7 deletions(-) create mode 100644 res/values/cm_strings.xml create mode 100644 res/values/lineage_config.xml create mode 100644 res/xml/backup_transport_settings.xml create mode 100644 src/com/android/settings/backup/transport/Transport.java create mode 100644 src/com/android/settings/backup/transport/TransportActivity.java create mode 100644 src/com/android/settings/backup/transport/TransportFragment.java create mode 100644 src/com/android/settings/backup/transport/TransportHelper.java create mode 100644 src/com/android/settings/backup/transport/TransportPreferenceController.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 69d000a9e89..90dd108efe9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5254,6 +5254,18 @@ + + + + + + + + diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml new file mode 100644 index 00000000000..a7c465a16c3 --- /dev/null +++ b/res/values/cm_strings.xml @@ -0,0 +1,21 @@ + + + + + Change backup provider + Select backup provider + diff --git a/res/values/lineage_config.xml b/res/values/lineage_config.xml new file mode 100644 index 00000000000..083a3d6e7cc --- /dev/null +++ b/res/values/lineage_config.xml @@ -0,0 +1,21 @@ + + + + + com.android.localtransport/.LocalTransport + + diff --git a/res/xml/backup_transport_settings.xml b/res/xml/backup_transport_settings.xml new file mode 100644 index 00000000000..cc8cc9ac143 --- /dev/null +++ b/res/xml/backup_transport_settings.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/src/com/android/settings/backup/BackupSettingsFragment.java b/src/com/android/settings/backup/BackupSettingsFragment.java index 7df19f54660..7fcbd63b7ce 100644 --- a/src/com/android/settings/backup/BackupSettingsFragment.java +++ b/src/com/android/settings/backup/BackupSettingsFragment.java @@ -42,6 +42,13 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } + @Override + public void onStart() { + super.onStart(); + // update information when we navigate back from TransportActivity + displayResourceTilesToScreen(getPreferenceScreen()); + } + /** * Get the tag string for logging. */ diff --git a/src/com/android/settings/backup/BackupSettingsPreferenceController.java b/src/com/android/settings/backup/BackupSettingsPreferenceController.java index 4e0e3b4e748..3208ae4fe76 100644 --- a/src/com/android/settings/backup/BackupSettingsPreferenceController.java +++ b/src/com/android/settings/backup/BackupSettingsPreferenceController.java @@ -30,24 +30,24 @@ public class BackupSettingsPreferenceController extends AbstractPreferenceContro implements PreferenceControllerMixin { private static final String BACKUP_SETTINGS = "backup_settings"; private static final String MANUFACTURER_SETTINGS = "manufacturer_backup"; - private Intent mBackupSettingsIntent; - private CharSequence mBackupSettingsTitle; - private String mBackupSettingsSummary; + private final BackupSettingsHelper settingsHelper; private Intent mManufacturerIntent; private String mManufacturerLabel; public BackupSettingsPreferenceController(Context context) { super(context); - BackupSettingsHelper settingsHelper = new BackupSettingsHelper(context); - mBackupSettingsIntent = settingsHelper.getIntentForBackupSettings(); - mBackupSettingsTitle = settingsHelper.getLabelForBackupSettings(); - mBackupSettingsSummary = settingsHelper.getSummaryForBackupSettings(); + settingsHelper = new BackupSettingsHelper(context); mManufacturerIntent = settingsHelper.getIntentProvidedByManufacturer(); mManufacturerLabel = settingsHelper.getLabelProvidedByManufacturer(); } @Override public void displayPreference(PreferenceScreen screen) { + // we don't get these in the constructor, so we can get updates for them later + Intent mBackupSettingsIntent = settingsHelper.getIntentForBackupSettings(); + CharSequence mBackupSettingsTitle = settingsHelper.getLabelForBackupSettings(); + String mBackupSettingsSummary = settingsHelper.getSummaryForBackupSettings(); + Preference backupSettings = screen.findPreference(BACKUP_SETTINGS); Preference manufacturerSettings = screen.findPreference(MANUFACTURER_SETTINGS); backupSettings.setIntent(mBackupSettingsIntent); diff --git a/src/com/android/settings/backup/transport/Transport.java b/src/com/android/settings/backup/transport/Transport.java new file mode 100644 index 00000000000..d2fd6e081f9 --- /dev/null +++ b/src/com/android/settings/backup/transport/Transport.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.backup.transport; + +class Transport { + final String name; + final CharSequence dataManagementLabel; + final CharSequence destinationString; + + Transport(String name, CharSequence dataManagementLabel, CharSequence destinationString) { + this.name = name; + this.dataManagementLabel = dataManagementLabel; + this.destinationString = destinationString; + } +} diff --git a/src/com/android/settings/backup/transport/TransportActivity.java b/src/com/android/settings/backup/transport/TransportActivity.java new file mode 100644 index 00000000000..1adcb9048a0 --- /dev/null +++ b/src/com/android/settings/backup/transport/TransportActivity.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.backup.transport; + +import android.os.Bundle; +import androidx.fragment.app.FragmentActivity; + +/** + * Activity to allow the user to choose the {@link android.app.backup.BackupTransport}. + * + * Set {@code config_backup_settings_intent} to {@code settings://com.android.settings.backup.transport} to activate. + * Don't forget to also set {@code config_backup_settings_label} or else it won't be shown. + */ +public class TransportActivity extends FragmentActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportFragmentManager().beginTransaction() + .replace(android.R.id.content, new TransportFragment()) + .commit(); + } + +} diff --git a/src/com/android/settings/backup/transport/TransportFragment.java b/src/com/android/settings/backup/transport/TransportFragment.java new file mode 100644 index 00000000000..6c6a8ce9957 --- /dev/null +++ b/src/com/android/settings/backup/transport/TransportFragment.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.backup.transport; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import com.android.settings.R; +import com.android.settings.backup.transport.TransportPreferenceController.OnTransportChangedListener; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.ArrayList; +import java.util.List; + +public class TransportFragment extends DashboardFragment implements OnTransportChangedListener { + + private static final String TAG = "TransportFragment"; + + /** + * Get the tag string for logging. + */ + @Override + protected String getLogTag() { + return TAG; + } + + /** + * Get the res id for static preference xml for this fragment. + */ + @Override + protected int getPreferenceScreenResId() { + return R.xml.backup_transport_settings; + } + + /** + * Get a list of {@link AbstractPreferenceController} for this fragment. + */ + @Override + protected List createPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + controllers.add(new TransportPreferenceController(context, this)); + return controllers; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.BACKUP_SETTINGS; + } + + @Override + public void onTransportChanged(String transportName) { + requireActivity().finish(); + } + +} diff --git a/src/com/android/settings/backup/transport/TransportHelper.java b/src/com/android/settings/backup/transport/TransportHelper.java new file mode 100644 index 00000000000..1ab5a596054 --- /dev/null +++ b/src/com/android/settings/backup/transport/TransportHelper.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.backup.transport; + +import android.app.backup.IBackupManager; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; +import androidx.annotation.Nullable; + +import com.android.settings.R; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Helper class for {@link TransportActivity} that interacts with {@link IBackupManager}. + */ +class TransportHelper { + private static final String TAG = "TransportHelper"; + + private final IBackupManager mBackupManager = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + + private Context mContext; + + TransportHelper(Context context) { + mContext = context; + } + + List getTransports() { + String[] backupTransports = getBackupTransports(); + if (backupTransports == null) return Collections.emptyList(); + ArrayList transports = new ArrayList<>(backupTransports.length); + String[] ignoredTransports = mContext.getResources().getStringArray( + R.array.config_ignored_backup_transports); + for (String name : getBackupTransports()) { + boolean ignored = false; + for (String ignoredTransport : ignoredTransports) { + if (name.equals(ignoredTransport)) ignored = true; + } + if (ignored) continue; + CharSequence label = getLabelFromBackupTransport(name); + if (label == null || label.length() == 0) label = name; + Transport transport = new Transport(name, label, getSummaryFromBackupTransport(name)); + transports.add(transport); + } + return transports; + } + + void selectTransport(String name) { + try { + mBackupManager.selectBackupTransport(name); + } catch (RemoteException e) { + Log.e(TAG, "Error selecting transport: " + name, e); + } + } + + @Nullable + private String[] getBackupTransports() { + try { + String[] transports = mBackupManager.listAllTransports(); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received all backup transports: " + Arrays.toString(transports)); + } + return transports; + } catch (RemoteException e) { + Log.e(TAG, "Error getting all backup transports", e); + } + return null; + } + + private CharSequence getLabelFromBackupTransport(String transport) { + try { + CharSequence label = mBackupManager.getDataManagementLabelForUser(UserHandle.myUserId(), transport); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received the backup settings label from " + transport + ": " + label); + } + return label; + } catch (RemoteException e) { + Log.e(TAG, "Error getting data management label for " + transport, e); + } + return null; + } + + private String getSummaryFromBackupTransport(String transport) { + try { + String summary = mBackupManager.getDestinationString(transport); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received the backup settings summary from " + transport + ": " + summary); + } + return summary; + } catch (RemoteException e) { + Log.e(TAG, "Error getting data management summary", e); + } + return null; + } +} diff --git a/src/com/android/settings/backup/transport/TransportPreferenceController.java b/src/com/android/settings/backup/transport/TransportPreferenceController.java new file mode 100644 index 00000000000..1dc5a511911 --- /dev/null +++ b/src/com/android/settings/backup/transport/TransportPreferenceController.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * 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.backup.transport; + +import android.content.Context; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import com.android.settingslib.core.AbstractPreferenceController; + +public class TransportPreferenceController extends AbstractPreferenceController { + + interface OnTransportChangedListener { + void onTransportChanged(String transportName); + } + + private final OnTransportChangedListener listener; + private final TransportHelper transportHelper; + + public TransportPreferenceController(Context context, OnTransportChangedListener listener) { + super(context); + this.listener = listener; + transportHelper = new TransportHelper(context); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + for (Transport transport : transportHelper.getTransports()) { + screen.addPreference(getPreferenceForTransport(transport)); + } + } + + private Preference getPreferenceForTransport(Transport transport) { + Preference p = new Preference(mContext); + p.setTitle(transport.dataManagementLabel); + p.setSummary(transport.destinationString); + p.setIconSpaceReserved(false); + p.setOnPreferenceClickListener(preference -> { + transportHelper.selectTransport(transport.name); + listener.onTransportChanged(transport.name); + return true; + }); + return p; + } + + /** + * Returns true if preference is available (should be displayed) + */ + @Override + public boolean isAvailable() { + return true; + } + + /** + * Returns the key for this preference. + */ + @Override + public String getPreferenceKey() { + return null; + } +} From 01aaf83cb8f14aefd04d07cc67a1a22e47f6cf24 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Sat, 9 Oct 2021 20:34:55 +0200 Subject: [PATCH 009/350] Settings: Put device info header above "Basic info" Change-Id: I8db12e882c16d6afa0e04bc1c4e93f7ec99960ed --- res/xml/my_device_info.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/res/xml/my_device_info.xml b/res/xml/my_device_info.xml index aec064b599f..36527f6dfe0 100644 --- a/res/xml/my_device_info.xml +++ b/res/xml/my_device_info.xml @@ -23,10 +23,9 @@ + android:selectable="false"/> Date: Fri, 8 Sep 2017 00:22:33 -0700 Subject: [PATCH 010/350] Settings: Depend on lineage platform library Change-Id: I9f23ed70ba4eba9a0008c33ecc4b048c23ca9be3 --- Android.bp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Android.bp b/Android.bp index cb898bef834..2422a65095b 100644 --- a/Android.bp +++ b/Android.bp @@ -110,6 +110,9 @@ android_library { "device_policy_aconfig_flags_lib", "kotlinx-coroutines-core", "kotlinx-coroutines-android", + + // Lineage dependencies + "org.lineageos.platform.internal", ], plugins: ["androidx.room_room-compiler-plugin"], From 0fbb0f46ea6ab4ae1446514a2473ed31000655c5 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Sun, 26 Aug 2018 15:00:21 +0200 Subject: [PATCH 011/350] Settings: Add LineageOS entries into device info Co-authored-by: Harry Youd Co-authored-by: Kevin F. Haggerty Co-authored-by: Timi Co-authored-by: Tobias Tefke Change-Id: I8d4c6869f8dfa34a96567bec18f9f8276b66a64e --- res/xml/firmware_version.xml | 25 ++++ res/xml/my_device_info.xml | 11 +- .../LineageBuildDatePreferenceController.java | 45 ++++++ ...ecurityPatchLevelPreferenceController.java | 73 ++++++++++ ...eageVersionDetailPreferenceController.java | 129 ++++++++++++++++++ 5 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 src/com/android/settings/deviceinfo/firmwareversion/LineageBuildDatePreferenceController.java create mode 100644 src/com/android/settings/deviceinfo/firmwareversion/LineageVendorSecurityPatchLevelPreferenceController.java create mode 100644 src/com/android/settings/deviceinfo/firmwareversion/LineageVersionDetailPreferenceController.java diff --git a/res/xml/firmware_version.xml b/res/xml/firmware_version.xml index 41f7733cfb2..99243a65d4d 100644 --- a/res/xml/firmware_version.xml +++ b/res/xml/firmware_version.xml @@ -30,6 +30,14 @@ settings:searchable="false" settings:controller="com.android.settings.deviceinfo.firmwareversion.FirmwareVersionDetailPreferenceController"/> + + + + + + + + + + + + = (SystemClock.uptimeMillis() - DELAY_TIMER_MILLIS)) { + if (mUserManager.hasUserRestriction(UserManager.DISALLOW_FUN)) { + if (mFunDisallowedAdmin != null && !mFunDisallowedBySystem) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, + mFunDisallowedAdmin); + } + Log.d(TAG, "Sorry, no fun for you!"); + return true; + } + + final Intent intent = new Intent(Intent.ACTION_MAIN) + .setClassName(PLATLOGO_PACKAGE_NAME, PLATLOGO_ACTIVITY_CLASS); + try { + mContext.startActivity(intent); + } catch (Exception e) { + Log.e(TAG, "Unable to start activity " + intent.toString()); + } + } + return true; + } + + /** + * Copies the array onto itself to remove the oldest hit. + */ + @VisibleForTesting + void arrayCopy() { + System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); + } + + @VisibleForTesting + void initializeAdminPermissions() { + mFunDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId()); + mFunDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( + mContext, UserManager.DISALLOW_FUN, UserHandle.myUserId()); + } +} From f9d78c1627a8114e3b5d081a7b5baf9ef23b90ad Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Tue, 30 Aug 2022 09:43:29 +0200 Subject: [PATCH 012/350] Settings: Depend on LineagePreferenceLib Change-Id: I553ae8e2a3eaaf7f13551180c97cd369f67ad14d --- Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.bp b/Android.bp index 2422a65095b..64ffec8d24b 100644 --- a/Android.bp +++ b/Android.bp @@ -113,6 +113,7 @@ android_library { // Lineage dependencies "org.lineageos.platform.internal", + "LineagePreferenceLib", ], plugins: ["androidx.room_room-compiler-plugin"], From 8fad6715487a59f36a1aefccfc34bcbdb33a17d6 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 26 Sep 2016 02:53:06 -0700 Subject: [PATCH 013/350] Settings: Add rotation settings * Links to LineageParts Change-Id: I896c53089fca0ee3b0e867144bb1d72fe8946eaf --- res/values/lineage_config.xml | 2 +- res/xml/display_settings.xml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/res/values/lineage_config.xml b/res/values/lineage_config.xml index 083a3d6e7cc..3836ba4557d 100644 --- a/res/values/lineage_config.xml +++ b/res/values/lineage_config.xml @@ -1,6 +1,6 @@ + + Rooted debugging + Allow running Android debugging as root + Change backup provider Select backup provider diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 866a529dd8b..52542efc442 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -189,6 +189,12 @@ android:summary="@string/enable_adb_wireless_summary" settings:keywords="@string/keywords_adb_wireless" /> + + buildPreferenceControllers(Con controllers.add(new AdbPreferenceController(context, fragment)); controllers.add(new ClearAdbKeysPreferenceController(context, fragment)); controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle)); + controllers.add(new AdbRootPreferenceController(context, fragment)); controllers.add(new AdbAuthorizationTimeoutPreferenceController(context)); controllers.add(new LocalTerminalPreferenceController(context)); controllers.add(new BugReportInPowerPreferenceController(context)); From dadafd414c91642b2051e0e74bf7bea3e5533be6 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Tue, 21 Dec 2021 23:50:23 +0200 Subject: [PATCH 016/350] Settings: Hide "Mainline module version" preference * Remove misleading titled preference (Google Play system update) * This is always set to current android version for AOSP Change-Id: Ie9799e87de2f3a3421ff501ed8937f5f684458ee --- res/xml/firmware_version.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/xml/firmware_version.xml b/res/xml/firmware_version.xml index 99243a65d4d..4eb25fd5786 100644 --- a/res/xml/firmware_version.xml +++ b/res/xml/firmware_version.xml @@ -54,12 +54,14 @@ settings:controller="com.android.settings.deviceinfo.firmwareversion.LineageVendorSecurityPatchLevelPreferenceController"/> + Date: Thu, 22 Feb 2018 15:27:46 +0100 Subject: [PATCH 017/350] Settings: Add LineageOS legal info Open up the LineageOS legal info in the browser. Change-Id: I263ccc0509e275d17512528deb606341d58e7a0d Ticket-Id: CYNGNOS-1895 Signed-off-by: Roman Birg --- res/values/cm_strings.xml | 3 + res/xml/about_legal.xml | 6 ++ .../LineageLicensePreferenceController.java | 63 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/com/android/settings/deviceinfo/legal/LineageLicensePreferenceController.java diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 721ea2fe8d9..5e106da48e6 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -22,4 +22,7 @@ Change backup provider Select backup provider + + + LineageOS legal diff --git a/res/xml/about_legal.xml b/res/xml/about_legal.xml index d30ceaa280b..a36ab66a601 100644 --- a/res/xml/about_legal.xml +++ b/res/xml/about_legal.xml @@ -35,6 +35,12 @@ android:title="@string/license_title" settings:controller="com.android.settings.deviceinfo.legal.LicensePreferenceController" /> + + + { + mContext.startActivity(getIntent()); + return true; + }); + } + } + + @Override + public int getAvailabilityStatus() { + if (getIntent().resolveActivity(mContext.getPackageManager()) != null) { + return AVAILABLE; + } else { + return UNSUPPORTED_ON_DEVICE; + } + } + + private Intent getIntent() { + return new Intent(Intent.ACTION_VIEW, + Uri.parse(SystemProperties.get(PROPERTY_LINEAGE_LICENSE_URL))); + } +} From 73e3b3163f78395a1ef9efb95db1773fb79b7c9a Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Wed, 26 Dec 2012 11:33:38 +0530 Subject: [PATCH 018/350] One does not simply become a Developer Co-authored-by: Michael Bestas Change-Id: I64ab4d7ab9d8334275d2e4d493b716a8fc73ab4e --- res/values/cm_strings.xml | 10 ++++++++++ .../deviceinfo/BuildNumberPreferenceController.java | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 5e106da48e6..41a7cbb8c4f 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -25,4 +25,14 @@ LineageOS legal + + + {count, plural, + =1 {You are now # step away from enabling development settings.} + other {You are now # steps away from enabling development settings.} + } + + You have enabled development settings! + + No need, you have already enabled development settings. diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java index 6fe3ca4521b..4189081e20e 100644 --- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java @@ -183,7 +183,7 @@ public boolean handlePreferenceTreeClick(Preference preference) { } mDevHitToast = Toast.makeText(mContext, StringUtil.getIcuPluralsString(mContext, mDevHitCountdown, - R.string.show_dev_countdown), + R.string.show_dev_countdown_cm), Toast.LENGTH_SHORT); mDevHitToast.show(); } @@ -198,7 +198,7 @@ public boolean handlePreferenceTreeClick(Preference preference) { if (mDevHitToast != null) { mDevHitToast.cancel(); } - mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already, + mDevHitToast = Toast.makeText(mContext, R.string.show_dev_already_cm, Toast.LENGTH_LONG); mDevHitToast.show(); mMetricsFeatureProvider.action( @@ -237,7 +237,7 @@ private void enableDevelopmentSettings() { if (mDevHitToast != null) { mDevHitToast.cancel(); } - mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on, + mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on_cm, Toast.LENGTH_LONG); mDevHitToast.show(); From a29dca94cd929509d2667168cc5af9cee99ea092 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Thu, 5 May 2022 01:38:53 +0300 Subject: [PATCH 019/350] Don't reset A2DP offload when disabling developer settings * We need to disable A2DP HW offload on FP4 for now, however disabling developer settings would enable offload again and break SBC and AAC audio. Just remove this section and allow this setting to persist after disabling developer settings. Change-Id: I5b65b3887d68ac684d7ea78de0ee89fb7db9bb0f --- ...oothA2dpHwOffloadPreferenceController.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java index f460b9e33fe..7d4b7a6fdda 100644 --- a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java +++ b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java @@ -74,23 +74,10 @@ public void updateState(Preference preference) { } } - @Override - protected void onDeveloperOptionsSwitchDisabled() { - super.onDeveloperOptionsSwitchDisabled(); - final boolean offloadSupported = - SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false); - if (offloadSupported) { - ((TwoStatePreference) mPreference).setChecked(false); - SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, "false"); - } - } - public boolean isDefaultValue() { - final boolean offloadSupported = - SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false); - final boolean offloadDisabled = - SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false); - return offloadSupported ? !offloadDisabled : true; + // Always return true here to avoid needing to reboot when disabling + // developer options, since we aren't turning this off when doing so anymore. + return true; } /** From 25e59205b924c69feace6cc31c8e8a50d86880c1 Mon Sep 17 00:00:00 2001 From: Sam Mortimer Date: Mon, 7 Nov 2016 23:38:08 -0800 Subject: [PATCH 020/350] Settings: Add LineageParts charging sound settings preference Replace the existing AOSP setting with this. Change-Id: I13894a86bcfad8d1dfaedc2dcbca6ad70f651b5c --- res/xml/sound_settings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/xml/sound_settings.xml b/res/xml/sound_settings.xml index 15312c8b067..b49345d698e 100644 --- a/res/xml/sound_settings.xml +++ b/res/xml/sound_settings.xml @@ -186,6 +186,12 @@ android:title="@string/charging_sounds_title" android:order="-40"/> + + + Date: Fri, 1 Aug 2014 18:34:15 +0300 Subject: [PATCH 021/350] Settings: Fix the failing strings Having double quotes around the strings causes crowdin to export translations with unescaped quotes. Removing them has no side effect and fixes crowdin export. Change-Id: I93865d793eef711f8738e00e843e9d4670724c66 --- res/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index f92fd2aca84..18efc94b3bb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3500,11 +3500,11 @@ Erase all data (factory reset) - "
  • Music
  • \n
  • Photos
  • \n
  • Other user data
  • "
    +
  • Music
  • \n
  • Photos
  • \n
  • Other user data
  • - "
  • eSIMs
  • "
    +
  • eSIMs
  • - "\n\nThis will not cancel your mobile service plan. + \n\nThis will not cancel your mobile service plan. All of your personal information and downloaded apps will be deleted. You can\u2019t undo this action. From 1aa106b02b698a377905a394138ac54cb24e960d Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Thu, 12 Dec 2013 05:40:54 +0200 Subject: [PATCH 022/350] Settings: Forward port lock pattern grid size (2/2) Co-authored-by: Alberto97 Co-authored-by: d34d Co-authored-by: Dhina17 Co-authored-by: LuK1337 Co-authored-by: Roman Birg Change-Id: I7078d703c218cd096d9b77c003a94b52fbce6322 --- AndroidManifest.xml | 4 + res/drawable/ic_security_pattern_3x3.xml | 26 +++ res/drawable/ic_security_pattern_4x4.xml | 26 +++ res/drawable/ic_security_pattern_5x5.xml | 26 +++ res/drawable/ic_security_pattern_6x6.xml | 26 +++ res/values/cm_strings.xml | 7 + res/values/lineage_styles.xml | 23 +++ res/xml/security_settings_pattern_size.xml | 44 ++++++ .../settings/password/ChooseLockPassword.java | 3 +- .../settings/password/ChooseLockPattern.java | 37 +++-- .../password/ChooseLockPatternSize.java | 149 ++++++++++++++++++ .../password/ChooseLockSettingsHelper.java | 7 +- .../password/ConfirmLockPassword.java | 3 +- .../settings/password/ConfirmLockPattern.java | 12 +- .../password/SaveAndFinishWorker.java | 9 +- .../password/SetupChooseLockPattern.java | 3 +- 16 files changed, 383 insertions(+), 22 deletions(-) create mode 100644 res/drawable/ic_security_pattern_3x3.xml create mode 100644 res/drawable/ic_security_pattern_4x4.xml create mode 100644 res/drawable/ic_security_pattern_5x5.xml create mode 100644 res/drawable/ic_security_pattern_6x6.xml create mode 100644 res/values/lineage_styles.xml create mode 100644 res/xml/security_settings_pattern_size.xml create mode 100644 src/com/android/settings/password/ChooseLockPatternSize.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 90dd108efe9..35d5710613f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2916,6 +2916,10 @@ android:enableOnBackInvokedCallback="false" android:excludeFromRecents="true" /> + + + + + diff --git a/res/drawable/ic_security_pattern_4x4.xml b/res/drawable/ic_security_pattern_4x4.xml new file mode 100644 index 00000000000..92c580f5e2c --- /dev/null +++ b/res/drawable/ic_security_pattern_4x4.xml @@ -0,0 +1,26 @@ + + + + diff --git a/res/drawable/ic_security_pattern_5x5.xml b/res/drawable/ic_security_pattern_5x5.xml new file mode 100644 index 00000000000..7b4dabaad79 --- /dev/null +++ b/res/drawable/ic_security_pattern_5x5.xml @@ -0,0 +1,26 @@ + + + + diff --git a/res/drawable/ic_security_pattern_6x6.xml b/res/drawable/ic_security_pattern_6x6.xml new file mode 100644 index 00000000000..1861284dbbf --- /dev/null +++ b/res/drawable/ic_security_pattern_6x6.xml @@ -0,0 +1,26 @@ + + + + diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 41a7cbb8c4f..286d50e8fd7 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -35,4 +35,11 @@ You have enabled development settings! No need, you have already enabled development settings. + + + 3 \u00d7 3 + 4 \u00d7 4 + 5 \u00d7 5 + 6 \u00d7 6 + Choose a pattern size diff --git a/res/values/lineage_styles.xml b/res/values/lineage_styles.xml new file mode 100644 index 00000000000..1ce2e23597b --- /dev/null +++ b/res/values/lineage_styles.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/res/xml/security_settings_pattern_size.xml b/res/xml/security_settings_pattern_size.xml new file mode 100644 index 00000000000..bb0cb238f2f --- /dev/null +++ b/res/xml/security_settings_pattern_size.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index a6453005ab9..da75b8136f3 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -1086,7 +1086,8 @@ private void startSaveAndFinish() { mUserId); mSaveAndFinishWorker.start(mLockPatternUtils, - mChosenPassword, mCurrentCredential, mUserId); + mChosenPassword, mCurrentCredential, mUserId, + mLockPatternUtils.getLockPatternSize(mUserId)); } @Override diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index c331991a486..a3afe19670a 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -102,7 +102,8 @@ public static class IntentBuilder { private final Intent mIntent; public IntentBuilder(Context context) { - mIntent = new Intent(context, ChooseLockPattern.class); + mIntent = new Intent(context, ChooseLockPatternSize.class); + mIntent.putExtra("className", ChooseLockPattern.class.getName()); mIntent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, false); } @@ -211,19 +212,14 @@ public static class ChooseLockPatternFragment extends InstrumentedFragment protected FooterButton mSkipOrClearButton; protected FooterButton mNextButton; @VisibleForTesting protected LockscreenCredential mChosenPattern; + private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT; private ColorStateList mDefaultHeaderColorList; private View mSudContent; /** * The patten used during the help screen to show how to draw a pattern. */ - private final List mAnimatePattern = - Collections.unmodifiableList(Lists.newArrayList( - LockPatternView.Cell.of(0, 0), - LockPatternView.Cell.of(0, 1), - LockPatternView.Cell.of(1, 1), - LockPatternView.Cell.of(2, 1) - )); + private List mAnimatePattern; @Override public void onActivityResult(int requestCode, int resultCode, @@ -268,12 +264,13 @@ public void onPatternCleared() { mLockPatternView.removeCallbacks(mClearPatternRunnable); } - public void onPatternDetected(List pattern) { + public void onPatternDetected(List pattern, + byte patternSize) { if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) { if (mChosenPattern == null) throw new IllegalStateException( "null chosen pattern in stage 'need to confirm"); try (LockscreenCredential confirmPattern = - LockscreenCredential.createPattern(pattern)) { + LockscreenCredential.createPattern(pattern, patternSize)) { if (mChosenPattern.equals(confirmPattern)) { updateStage(Stage.ChoiceConfirmed); } else { @@ -284,7 +281,8 @@ public void onPatternDetected(List pattern) { if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { updateStage(Stage.ChoiceTooShort); } else { - mChosenPattern = LockscreenCredential.createPattern(pattern); + mChosenPattern = LockscreenCredential.createPattern( + pattern, patternSize); updateStage(Stage.FirstChoiceValid); } } else { @@ -542,6 +540,16 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mSudContent.setPadding(mSudContent.getPaddingLeft(), 0, mSudContent.getPaddingRight(), 0); + mPatternSize = getActivity().getIntent().getByteExtra("pattern_size", + LockPatternUtils.PATTERN_SIZE_DEFAULT); + LockPatternView.Cell.updateSize(mPatternSize); + mAnimatePattern = Collections.unmodifiableList(Lists.newArrayList( + LockPatternView.Cell.of(0, 0, mPatternSize), + LockPatternView.Cell.of(0, 1, mPatternSize), + LockPatternView.Cell.of(1, 1, mPatternSize), + LockPatternView.Cell.of(2, 1, mPatternSize) + )); + return layout; } @@ -555,6 +563,8 @@ public void onViewCreated(View view, Bundle savedInstanceState) { mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern); mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener); mLockPatternView.setFadePattern(false); + mLockPatternView.setLockPatternUtils(mLockPatternUtils); + mLockPatternView.setLockPatternSize(mPatternSize); mFooterText = (TextView) view.findViewById(R.id.footerText); @@ -601,6 +611,9 @@ public void onViewCreated(View view, Bundle savedInstanceState) { // restore from previous state mChosenPattern = savedInstanceState.getParcelable(KEY_PATTERN_CHOICE); mCurrentCredential = savedInstanceState.getParcelable(KEY_CURRENT_PATTERN); + mLockPatternView.setPattern(DisplayMode.Correct, + LockPatternUtils.byteArrayToPattern( + mChosenPattern.getCredential(), mPatternSize)); updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]); @@ -859,7 +872,7 @@ private void startSaveAndFinish() { } } mSaveAndFinishWorker.start(mLockPatternUtils, - mChosenPattern, mCurrentCredential, mUserId); + mChosenPattern, mCurrentCredential, mUserId, mPatternSize); } @Override diff --git a/src/com/android/settings/password/ChooseLockPatternSize.java b/src/com/android/settings/password/ChooseLockPatternSize.java new file mode 100644 index 00000000000..eed899a16cb --- /dev/null +++ b/src/com/android/settings/password/ChooseLockPatternSize.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012-2013 The CyanogenMod 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.password; + +import android.content.Intent; +import android.content.res.Resources.Theme; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import androidx.preference.Preference; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.SetupWizardUtils; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.utils.SettingsDividerItemDecoration; + +import com.google.android.setupdesign.GlifPreferenceLayout; +import com.google.android.setupdesign.util.ThemeHelper; + +import org.lineageos.internal.logging.LineageMetricsLogger; + +public class ChooseLockPatternSize extends SettingsActivity { + + @Override + public Intent getIntent() { + Intent modIntent = new Intent(super.getIntent()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPatternSizeFragment.class.getName()); + return modIntent; + } + + @Override + protected boolean isValidFragment(String fragmentName) { + if (ChooseLockPatternSizeFragment.class.getName().equals(fragmentName)) return true; + return false; + } + + @Override + protected boolean isToolbarEnabled() { + return false; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(SetupWizardUtils.getTheme(this, getIntent())); + ThemeHelper.trySetDynamicColor(this); + super.onCreate(savedInstanceState); + findViewById(R.id.content_parent).setFitsSystemWindows(false); + } + + public static class ChooseLockPatternSizeFragment extends SettingsPreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (!(getActivity() instanceof ChooseLockPatternSize)) { + throw new SecurityException("Fragment contained in wrong activity"); + } + addPreferencesFromResource(R.xml.security_settings_pattern_size); + } + + @Override + public boolean onPreferenceTreeClick(Preference preference) { + final String key = preference.getKey(); + + byte patternSize; + if ("lock_pattern_size_4".equals(key)) { + patternSize = 4; + } else if ("lock_pattern_size_5".equals(key)) { + patternSize = 5; + } else if ("lock_pattern_size_6".equals(key)) { + patternSize = 6; + } else { + patternSize = 3; + } + + Bundle extras = getActivity().getIntent().getExtras(); + Intent intent = new Intent(); + intent.setClassName(getActivity(), extras.getString("className")); + intent.putExtras(extras); + intent.putExtra("pattern_size", patternSize); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + startActivity(intent); + + finish(); + return true; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + GlifPreferenceLayout layout = (GlifPreferenceLayout) view; + layout.setDividerItemDecoration(new SettingsDividerItemDecoration(getContext())); + + layout.setIcon(getContext().getDrawable(R.drawable.ic_lock)); + + if (getActivity() != null) { + getActivity().setTitle(R.string.lock_settings_picker_pattern_size_message); + } + + layout.setHeaderText(R.string.lock_settings_picker_pattern_size_message); + + // Remove the padding on the start of the header text. + if (ThemeHelper.shouldApplyMaterialYouStyle(getContext())) { + final LinearLayout headerLayout = layout.findManagedViewById( + com.google.android.setupdesign.R.id.sud_layout_header); + if (headerLayout != null) { + headerLayout.setPadding(0, layout.getPaddingTop(), 0, + layout.getPaddingBottom()); + } + } + + // Use the dividers in SetupWizardRecyclerLayout. Suppress the dividers in + // PreferenceFragment. + setDivider(null); + } + + @Override + public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, + Bundle savedInstanceState) { + GlifPreferenceLayout layout = (GlifPreferenceLayout) parent; + return layout.onCreateRecyclerView(inflater, parent, savedInstanceState); + } + + @Override + public int getMetricsCategory() { + return LineageMetricsLogger.CHOOSE_LOCK_PATTERN_SIZE; + } + } +} diff --git a/src/com/android/settings/password/ChooseLockSettingsHelper.java b/src/com/android/settings/password/ChooseLockSettingsHelper.java index 91875cc88d6..0f907cd0eb1 100644 --- a/src/com/android/settings/password/ChooseLockSettingsHelper.java +++ b/src/com/android/settings/password/ChooseLockSettingsHelper.java @@ -475,6 +475,9 @@ private boolean launchConfirmationActivity(int request, CharSequence title, Char intent.setClassName(SETTINGS_PACKAGE_NAME, activityClass.getName()); intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE); + if (userId == LockPatternUtils.USER_FRP) { + intent.putExtra("className", ConfirmLockPattern.class.getName()); + } Intent inIntent = mFragment != null ? mFragment.getActivity().getIntent() : mActivity.getIntent(); @@ -555,7 +558,9 @@ private Optional> determineAppropriateActivityClass(boolean returnCrede ? ConfirmLockPassword.InternalActivity.class : ConfirmLockPassword.class); case KeyguardManager.PATTERN: - return Optional.of(returnCredentials || forceVerifyPath + return Optional.of(userId == LockPatternUtils.USER_FRP + ? ChooseLockPatternSize.class + : returnCredentials || forceVerifyPath ? ConfirmLockPattern.InternalActivity.class : ConfirmLockPattern.class); } diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java index b9b1810da25..35f6a82e3c0 100644 --- a/src/com/android/settings/password/ConfirmLockPassword.java +++ b/src/com/android/settings/password/ConfirmLockPassword.java @@ -628,7 +628,8 @@ public void onRemoteLockscreenValidationResult( mLockPatternUtils, mRemoteLockscreenValidationFragment.getLockscreenCredential(), /* currentCredential= */ null, - mEffectiveUserId); + mEffectiveUserId, + mLockPatternUtils.getLockPatternSize(mEffectiveUserId)); } else { mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(), /* timeoutMs= */ 0, mEffectiveUserId); diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java index 3415478c912..06fdc496ff8 100644 --- a/src/com/android/settings/password/ConfirmLockPattern.java +++ b/src/com/android/settings/password/ConfirmLockPattern.java @@ -115,6 +115,7 @@ public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBa private DisappearAnimationUtils mDisappearAnimationUtils; private boolean mIsManagedProfile; + private byte mPatternSize; // required constructor for fragments public ConfirmLockPatternFragment() { @@ -141,6 +142,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mSudContent.setPadding(mSudContent.getPaddingLeft(), 0, mSudContent.getPaddingRight(), 0); mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId); + mPatternSize = mLockPatternUtils.getLockPatternSize(mEffectiveUserId); // make it so unhandled touch events within the unlock screen go to the // lock pattern view. @@ -155,6 +157,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mDetailsText = intent.getCharSequenceExtra( ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT); mCheckBoxLabel = intent.getCharSequenceExtra(KeyguardManager.EXTRA_CHECKBOX_LABEL); + mPatternSize = intent.getByteExtra("pattern_size", mPatternSize); } if (TextUtils.isEmpty(mHeaderText) && mIsManagedProfile) { mHeaderText = mDevicePolicyManager.getOrganizationNameForUser(mUserId); @@ -162,6 +165,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( mEffectiveUserId)); + mLockPatternView.setLockPatternSize(mPatternSize); mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener); mLockPatternView.setOnTouchListener((v, event) -> { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -497,14 +501,15 @@ public void onPatternCellAdded(List pattern) { } - public void onPatternDetected(List pattern) { + public void onPatternDetected(List pattern, byte patternSize) { if (mPendingLockCheck != null || mDisappearing) { return; } mLockPatternView.setEnabled(false); - final LockscreenCredential credential = LockscreenCredential.createPattern(pattern); + final LockscreenCredential credential = LockscreenCredential.createPattern(pattern, + patternSize); if (mRemoteValidation) { validateGuess(credential); @@ -643,7 +648,8 @@ public void onRemoteLockscreenValidationResult( mLockPatternUtils, mRemoteLockscreenValidationFragment.getLockscreenCredential(), /* currentCredential= */ null, - mEffectiveUserId); + mEffectiveUserId, + mLockPatternUtils.getLockPatternSize(mEffectiveUserId)); } else { mCredentialCheckResultTracker.setResult(/* matched= */ true, new Intent(), /* timeoutMs= */ 0, mEffectiveUserId); diff --git a/src/com/android/settings/password/SaveAndFinishWorker.java b/src/com/android/settings/password/SaveAndFinishWorker.java index 40054b77645..5592071e7a4 100644 --- a/src/com/android/settings/password/SaveAndFinishWorker.java +++ b/src/com/android/settings/password/SaveAndFinishWorker.java @@ -53,6 +53,7 @@ public class SaveAndFinishWorker extends Fragment { private LockscreenCredential mUnificationProfileCredential; private LockscreenCredential mChosenCredential; private LockscreenCredential mCurrentCredential; + private byte mPatternSize; private boolean mBlocking; @@ -76,9 +77,10 @@ public SaveAndFinishWorker setListener(Listener listener) { @VisibleForTesting void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential, - LockscreenCredential currentCredential, int userId) { + LockscreenCredential currentCredential, int userId, byte patternSize) { mUtils = utils; mUserId = userId; + mPatternSize = patternSize; // This will be a no-op for non managed profiles. mWasSecureBefore = mUtils.isSecure(mUserId); mFinished = false; @@ -90,8 +92,8 @@ void prepare(LockPatternUtils utils, LockscreenCredential chosenCredential, } public void start(LockPatternUtils utils, LockscreenCredential chosenCredential, - LockscreenCredential currentCredential, int userId) { - prepare(utils, chosenCredential, currentCredential, userId); + LockscreenCredential currentCredential, int userId, byte patternSize) { + prepare(utils, chosenCredential, currentCredential, userId, patternSize); if (mBlocking) { finish(saveAndVerifyInBackground().second); } else { @@ -107,6 +109,7 @@ public void start(LockPatternUtils utils, LockscreenCredential chosenCredential, @VisibleForTesting Pair saveAndVerifyInBackground() { final int userId = mUserId; + mUtils.setLockPatternSize(mPatternSize, userId); try { if (!mUtils.setLockCredential(mChosenCredential, mCurrentCredential, userId)) { return Pair.create(false, null); diff --git a/src/com/android/settings/password/SetupChooseLockPattern.java b/src/com/android/settings/password/SetupChooseLockPattern.java index 55b3847125e..e0e598064ff 100644 --- a/src/com/android/settings/password/SetupChooseLockPattern.java +++ b/src/com/android/settings/password/SetupChooseLockPattern.java @@ -47,7 +47,8 @@ public class SetupChooseLockPattern extends ChooseLockPattern { public static Intent modifyIntentForSetup(Context context, Intent chooseLockPatternIntent) { - chooseLockPatternIntent.setClass(context, SetupChooseLockPattern.class); + chooseLockPatternIntent.setClass(context, ChooseLockPatternSize.class); + chooseLockPatternIntent.putExtra("className", SetupChooseLockPattern.class.getName()); return chooseLockPatternIntent; } From c141746f35db7dbaa8a951b1f1d9540f6e898e1e Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Sat, 4 Apr 2015 04:43:43 +0300 Subject: [PATCH 023/350] Settings: Forward port pattern visibility settings (2/2) Change-Id: Ic627953c5df854c442671a98b5da539b994da18b --- res/values/cm_strings.xml | 5 ++ res/xml/screen_lock_settings.xml | 11 ++- ...ractPatternSwitchPreferenceController.java | 72 +++++++++++++++++++ ...atternDotsVisiblePreferenceController.java | 41 +++++++++++ ...tternErrorVisiblePreferenceController.java | 41 +++++++++++ .../PatternVisiblePreferenceController.java | 41 ++--------- .../screenlock/ScreenLockSettings.java | 4 ++ 7 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 src/com/android/settings/security/screenlock/AbstractPatternSwitchPreferenceController.java create mode 100644 src/com/android/settings/security/screenlock/PatternDotsVisiblePreferenceController.java create mode 100644 src/com/android/settings/security/screenlock/PatternErrorVisiblePreferenceController.java diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 286d50e8fd7..2add3093a65 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -42,4 +42,9 @@ 5 \u00d7 5 6 \u00d7 6 Choose a pattern size + + + Show pattern error + + Show pattern dots diff --git a/res/xml/screen_lock_settings.xml b/res/xml/screen_lock_settings.xml index 19061d9b855..9d6b6cb2ad3 100644 --- a/res/xml/screen_lock_settings.xml +++ b/res/xml/screen_lock_settings.xml @@ -27,6 +27,14 @@ android:key="visiblepattern" android:title="@string/lockpattern_settings_enable_visible_pattern_title" /> + + + + - - + buildPreferenceControllers(Con context, MY_USER_ID, lockPatternUtils)); controllers.add(new PinPrivacyPreferenceController( context, MY_USER_ID, lockPatternUtils)); + controllers.add(new PatternErrorVisiblePreferenceController( + context, MY_USER_ID, lockPatternUtils)); + controllers.add(new PatternDotsVisiblePreferenceController( + context, MY_USER_ID, lockPatternUtils)); controllers.add(new PowerButtonInstantLockPreferenceController( context, MY_USER_ID, lockPatternUtils)); controllers.add(new LockAfterTimeoutPreferenceController( From 7a82d1308ed3cee0b9cb857f57d4602b7bcfa701 Mon Sep 17 00:00:00 2001 From: Luca Stefani Date: Fri, 12 Oct 2018 21:49:35 +0200 Subject: [PATCH 024/350] Settings: Check if we have any color modes declared in overlay * Checking for wide gamut support isn't always enough Change-Id: Ic7257e37f2eca97450eaf0ca1e330de7596352be --- .../settings/display/ColorModePreferenceController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/settings/display/ColorModePreferenceController.java b/src/com/android/settings/display/ColorModePreferenceController.java index 6cd4867b588..14a2241481c 100644 --- a/src/com/android/settings/display/ColorModePreferenceController.java +++ b/src/com/android/settings/display/ColorModePreferenceController.java @@ -28,8 +28,11 @@ public ColorModePreferenceController(Context context, String key) { @Override public int getAvailabilityStatus() { + final int[] availableColorModes = mContext.getResources().getIntArray( + com.android.internal.R.array.config_availableColorModes); return mContext.getSystemService(ColorDisplayManager.class) .isDeviceColorManaged() + && availableColorModes.length > 0 && !ColorDisplayManager.areAccessibilityTransformsEnabled(mContext) ? AVAILABLE : DISABLED_FOR_USER; } From 27b6142e9e0259bfdd910dc1bd58d507346791b5 Mon Sep 17 00:00:00 2001 From: Wang Han <416810799@qq.com> Date: Sun, 9 Sep 2018 11:27:15 +0800 Subject: [PATCH 025/350] Settings: Hide unsupported USB modes automatically * The roles are advertised by USB HAL so we can check for their status Change-Id: I5933d1a03f573af08b00039850173329b293448a --- .../connecteddevice/usb/UsbBackend.java | 24 +++++++++++++++++++ .../usb/UsbDetailsDataRoleController.java | 3 ++- .../usb/UsbDetailsPowerRoleController.java | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/connecteddevice/usb/UsbBackend.java b/src/com/android/settings/connecteddevice/usb/UsbBackend.java index d194499c88c..6e643f42fea 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbBackend.java +++ b/src/com/android/settings/connecteddevice/usb/UsbBackend.java @@ -165,6 +165,30 @@ public boolean areAllRolesSupported() { && mPortStatus.isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST); } + public boolean isSingleDataRoleSupported() { + return mPort != null && mPortStatus != null + && ((!mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST) + && !mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST)) + || (!mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE) + && !mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_DEVICE))); + } + + public boolean isSinglePowerRoleSupported() { + return mPort != null && mPortStatus != null + && ((!mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE) + && !mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST)) + || (!mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_DEVICE) + && !mPortStatus + .isRoleCombinationSupported(POWER_ROLE_SOURCE, DATA_ROLE_HOST))); + } + public static String usbFunctionsToString(long functions) { // TODO replace with UsbManager.usbFunctionsToString once supported by Roboelectric return Long.toBinaryString(functions); diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java index 8782c796140..8c291abd205 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java @@ -115,7 +115,8 @@ public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { @Override public boolean isAvailable() { - return !Utils.isMonkeyRunning(); + return !Utils.isMonkeyRunning() + && !mUsbBackend.isSingleDataRoleSupported(); } @Override diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java index f00435a0cab..9a14f3cde86 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsPowerRoleController.java @@ -126,7 +126,8 @@ public boolean onPreferenceClick(Preference preference) { @Override public boolean isAvailable() { - return !Utils.isMonkeyRunning(); + return !Utils.isMonkeyRunning() + && !mUsbBackend.isSinglePowerRoleSupported(); } @Override From 7d434c91e34a98767c8dfd7f4831fe93fe78edd1 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Fri, 14 Oct 2016 21:46:42 -0700 Subject: [PATCH 026/350] Settings: Add a RemotePreference for device-specific doze settings * If a device has its own panel, we should replace the item under display settings instead of having to clutter up the dashboard. * Devices that provide Doze settings typically implement own AOD pref, so hide AOSP one. * This uses the new RemotePreference API to allow the external app to update the view. * The action for this is "org.lineageos.settings.device.DOZE_SETTINGS". Co-authored-by: Bruno Martins Change-Id: I22fd7e6307b370807415ce2132bed4808b2befe1 --- res/xml/security_lockscreen_settings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/res/xml/security_lockscreen_settings.xml b/res/xml/security_lockscreen_settings.xml index 15d530357d9..eafb19748d9 100644 --- a/res/xml/security_lockscreen_settings.xml +++ b/res/xml/security_lockscreen_settings.xml @@ -124,6 +124,13 @@ android:summary="@string/doze_summary" settings:controller="com.android.settings.display.AmbientDisplayNotificationsPreferenceController" /> + + + +
    Show pattern dots + + + Navigation hint + Show navigation hint bar at the bottom of the screen diff --git a/res/xml/gesture_navigation_settings.xml b/res/xml/gesture_navigation_settings.xml index a4b5af9d848..31172afe92b 100644 --- a/res/xml/gesture_navigation_settings.xml +++ b/res/xml/gesture_navigation_settings.xml @@ -23,6 +23,13 @@ android:title="@string/gesture_settings_activity_title" settings:keywords="@string/keywords_gesture_navigation_settings"> + + Date: Tue, 24 Aug 2021 09:02:32 +0000 Subject: [PATCH 028/350] Settings: use Glif themed button bar Change-Id: I7b7c522730806ee9c8cc83e4f560a344a089cbec --- res/layout/settings_main_prefs.xml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/res/layout/settings_main_prefs.xml b/res/layout/settings_main_prefs.xml index 48352e2efa0..ceb52eeb84d 100644 --- a/res/layout/settings_main_prefs.xml +++ b/res/layout/settings_main_prefs.xml @@ -38,35 +38,37 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_weight="0" - android:visibility="gone"> + android:visibility="gone" + style="@style/SudGlifButtonBar">