Merge "Accessibility improvements" into ub-launcher3-qt-dev
diff --git a/res/layout/activity_clock_face_picker.xml b/res/layout/activity_clock_face_picker.xml
new file mode 100644
index 0000000..5a722dc
--- /dev/null
+++ b/res/layout/activity_clock_face_picker.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</FrameLayout>
diff --git a/robolectric_tests/src/com/android/customization/model/clock/BaseClockManagerTest.java b/robolectric_tests/src/com/android/customization/model/clock/BaseClockManagerTest.java
new file mode 100644
index 0000000..c96e7f8
--- /dev/null
+++ b/robolectric_tests/src/com/android/customization/model/clock/BaseClockManagerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.clock;
+
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.CustomizationManager.Callback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BaseClockManagerTest {
+
+ private static final String CURRENT_CLOCK = "current_clock";
+
+ @Mock ClockProvider mProvider;
+ private TestClockManager mManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mManager = new TestClockManager(mProvider);
+ }
+
+ @Test
+ public void testIsAvailable() {
+ // GIVEN that the ClockProvider is available
+ when(mProvider.isAvailable()).thenReturn(true);
+ // THEN the BaseClockManager is true
+ assertTrue(mManager.isAvailable());
+ }
+
+ @Test
+ public void testApply() {
+ final String id = "id";
+ Clockface clock = new Clockface.Builder().setId(id).build();
+
+ mManager.apply(clock, new Callback() {
+ @Override
+ public void onSuccess() {
+ //Nothing to do here, the test passed
+ }
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ fail("onError was called when grid had been applied successfully");
+ }
+ });
+
+ assertEquals(id, mManager.getClockId());
+ }
+
+ @Test
+ public void testFetch() {
+ mManager.fetchOptions(null, false);
+ verify(mProvider).fetch(eq(null), anyBoolean());
+ }
+
+ /**
+ * Testable BaseClockManager that provides basic implementations of abstract methods.
+ */
+ private static final class TestClockManager extends BaseClockManager {
+
+ private String mClockId;
+
+ TestClockManager(ClockProvider provider) {
+ super(provider);
+ }
+
+ String getClockId() {
+ return mClockId;
+ }
+
+ @Override
+ protected void handleApply(Clockface option, Callback callback) {
+ mClockId = option.getId();
+ callback.onSuccess();
+ }
+
+ @Override
+ protected String lookUpCurrentClock() {
+ return CURRENT_CLOCK;
+ }
+ }
+}
diff --git a/robolectric_tests/src/com/android/customization/model/clock/ClockManagerTest.java b/robolectric_tests/src/com/android/customization/model/clock/ClockManagerTest.java
new file mode 100644
index 0000000..d2ab43f
--- /dev/null
+++ b/robolectric_tests/src/com/android/customization/model/clock/ClockManagerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.clock;
+
+import static junit.framework.TestCase.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.provider.Settings.Secure;
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.CustomizationManager.Callback;
+
+import com.android.customization.module.ThemesUserEventLogger;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ClockManagerTest {
+
+ private static final String CLOCK_ID = "id";
+
+ @Mock ClockProvider mProvider;
+ @Mock ThemesUserEventLogger mLogger;
+ private ContentResolver mContentResolver;
+ private ClockManager mManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContentResolver = RuntimeEnvironment.application.getContentResolver();
+ mManager = new ClockManager(mContentResolver, mProvider, mLogger);
+ }
+
+ @Test
+ public void testApply() {
+ Clockface clock = new Clockface.Builder().setId(CLOCK_ID).build();
+
+ mManager.apply(clock, new Callback() {
+ @Override
+ public void onSuccess() {
+ //Nothing to do here, the test passed
+ }
+
+ @Override
+ public void onError(@Nullable Throwable throwable) {
+ fail("onError was called when grid had been applied successfully");
+ }
+ });
+
+ // THEN the clock id is written to secure settings.
+ assertEquals(CLOCK_ID, Secure.getString(mContentResolver, ClockManager.CLOCK_FACE_SETTING));
+ // AND the event is logged
+ verify(mLogger).logClockApplied(clock);
+ }
+
+ @Test
+ public void testGetCurrentClock() {
+ // GIVEN that secure settings contains a clock id
+ Secure.putString(mContentResolver, ClockManager.CLOCK_FACE_SETTING, CLOCK_ID);
+ // THEN the current clock is that id
+ assertEquals(CLOCK_ID, mManager.getCurrentClock());
+ }
+}
diff --git a/src/com/android/customization/model/clock/BaseClockManager.java b/src/com/android/customization/model/clock/BaseClockManager.java
new file mode 100644
index 0000000..3434780
--- /dev/null
+++ b/src/com/android/customization/model/clock/BaseClockManager.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.model.clock;
+
+import com.android.customization.model.CustomizationManager;
+
+/**
+ * {@link CustomizationManager} for clock faces.
+ */
+public abstract class BaseClockManager implements CustomizationManager<Clockface> {
+
+ private final ClockProvider mClockProvider;
+
+ public BaseClockManager(ClockProvider provider) {
+ mClockProvider = provider;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mClockProvider.isAvailable();
+ }
+
+ @Override
+ public void apply(Clockface option, Callback callback) {
+ handleApply(option, callback);
+ }
+
+ @Override
+ public void fetchOptions(OptionsFetchedListener<Clockface> callback, boolean reload) {
+ mClockProvider.fetch(callback, false);
+ }
+
+ /** Returns the ID of the current clock face, which may be null for the default clock face. */
+ String getCurrentClock() {
+ return lookUpCurrentClock();
+ }
+
+ /**
+ * Implement to apply the clock picked by the user for {@link BaseClockManager#apply}.
+ *
+ * @param option Clock option, containing ID of the clock, that the user picked.
+ * @param callback Report success and failure.
+ */
+ protected abstract void handleApply(Clockface option, Callback callback);
+
+ /**
+ * Implement to look up the current clock face for {@link BaseClockManager#getCurrentClock()}.
+ *
+ * @return ID of current clock. Can be null for the default clock face.
+ */
+ protected abstract String lookUpCurrentClock();
+}
diff --git a/src/com/android/customization/model/clock/ClockManager.java b/src/com/android/customization/model/clock/ClockManager.java
index 8390688..3744317 100644
--- a/src/com/android/customization/model/clock/ClockManager.java
+++ b/src/com/android/customization/model/clock/ClockManager.java
@@ -15,35 +15,31 @@
*/
package com.android.customization.model.clock;
-import android.content.Context;
+import android.content.ContentResolver;
import android.provider.Settings.Secure;
-import com.android.customization.model.CustomizationManager;
import com.android.customization.module.ThemesUserEventLogger;
-public class ClockManager implements CustomizationManager<Clockface> {
+/**
+ * {@link CustomizationManager} for clock faces that implements apply by writing to secure settings.
+ */
+public class ClockManager extends BaseClockManager {
// TODO: use constant from Settings.Secure
- private static final String CLOCK_FACE_SETTING = "lock_screen_custom_clock_face";
- private final ClockProvider mClockProvider;
- private final Context mContext;
+ static final String CLOCK_FACE_SETTING = "lock_screen_custom_clock_face";
+ private final ContentResolver mContentResolver;
private final ThemesUserEventLogger mEventLogger;
- public ClockManager(Context context, ClockProvider provider, ThemesUserEventLogger logger) {
- mClockProvider = provider;
- mContext = context;
+ public ClockManager(ContentResolver resolver, ClockProvider provider,
+ ThemesUserEventLogger logger) {
+ super(provider);
+ mContentResolver = resolver;
mEventLogger = logger;
}
@Override
- public boolean isAvailable() {
- return mClockProvider.isAvailable();
- }
-
- @Override
- public void apply(Clockface option, Callback callback) {
- boolean stored = Secure.putString(mContext.getContentResolver(),
- CLOCK_FACE_SETTING, option.getId());
+ protected void handleApply(Clockface option, Callback callback) {
+ boolean stored = Secure.putString(mContentResolver, CLOCK_FACE_SETTING, option.getId());
if (stored) {
mEventLogger.logClockApplied(option);
callback.onSuccess();
@@ -53,11 +49,7 @@
}
@Override
- public void fetchOptions(OptionsFetchedListener<Clockface> callback, boolean reload) {
- mClockProvider.fetch(callback, false);
- }
-
- public String getCurrentClock() {
- return Secure.getString(mContext.getContentResolver(), CLOCK_FACE_SETTING);
+ protected String lookUpCurrentClock() {
+ return Secure.getString(mContentResolver, CLOCK_FACE_SETTING);
}
}
diff --git a/src/com/android/customization/model/clock/Clockface.java b/src/com/android/customization/model/clock/Clockface.java
index 1e68552..9bdcaef 100644
--- a/src/com/android/customization/model/clock/Clockface.java
+++ b/src/com/android/customization/model/clock/Clockface.java
@@ -15,8 +15,6 @@
*/
package com.android.customization.model.clock;
-import android.content.Context;
-import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
@@ -54,7 +52,7 @@
@Override
public boolean isActive(CustomizationManager<Clockface> manager) {
- String currentClock = ((ClockManager) manager).getCurrentClock();
+ String currentClock = ((BaseClockManager) manager).getCurrentClock();
// Empty clock Id is the default system clock
return (TextUtils.isEmpty(currentClock) && TextUtils.isEmpty(mId))
|| (mId != null && mId.equals(currentClock));
diff --git a/src/com/android/customization/picker/ClockFacePickerActivity.java b/src/com/android/customization/picker/ClockFacePickerActivity.java
new file mode 100644
index 0000000..261f794
--- /dev/null
+++ b/src/com/android/customization/picker/ClockFacePickerActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.customization.picker;
+
+import android.content.Intent;
+import android.os.Bundle;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import com.android.customization.model.clock.BaseClockManager;
+import com.android.customization.model.clock.Clockface;
+import com.android.customization.model.clock.ContentProviderClockProvider;
+import com.android.customization.picker.clock.ClockFragment;
+import com.android.customization.picker.clock.ClockFragment.ClockFragmentHost;
+import com.android.wallpaper.R;
+
+/**
+ * Activity allowing for the clock face picker to be linked to from other setup flows.
+ *
+ * This should be used with startActivityForResult. The resulting intent contains an extra
+ * "clock_face_name" with the id of the picked clock face.
+ */
+public class ClockFacePickerActivity extends FragmentActivity implements ClockFragmentHost {
+
+ private static final String EXTRA_CLOCK_FACE_NAME = "clock_face_name";
+
+ private BaseClockManager mClockManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_clock_face_picker);
+
+ // Creating a class that overrides {@link ClockManager#apply} to return the clock id to the
+ // calling activity instead of putting the value into settings.
+ //
+ mClockManager = new BaseClockManager(
+ new ContentProviderClockProvider(ClockFacePickerActivity.this)) {
+
+ @Override
+ protected void handleApply(Clockface option, Callback callback) {
+ Intent result = new Intent();
+ result.putExtra(EXTRA_CLOCK_FACE_NAME, option.getId());
+ setResult(RESULT_OK, result);
+ callback.onSuccess();
+ }
+
+ @Override
+ protected String lookUpCurrentClock() {
+ return getIntent().getStringExtra(EXTRA_CLOCK_FACE_NAME);
+ }
+ };
+
+ final FragmentManager fm = getSupportFragmentManager();
+ final FragmentTransaction fragmentTransaction = fm.beginTransaction();
+ final ClockFragment clockFragment = ClockFragment.newInstance(getString(R.string.clock_title));
+ fragmentTransaction.replace(R.id.fragment_container, clockFragment);
+ fragmentTransaction.commitNow();
+ }
+
+ @Override
+ public BaseClockManager getClockManager() {
+ return mClockManager;
+ }
+}
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index 2ec91d6..dde061b 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -183,9 +183,8 @@
mSections.put(R.id.nav_theme, new ThemeSection(R.id.nav_theme, themeManager));
}
//Clock
- //ClockManager clockManager = new ClockManager(this, new ResourcesApkClockProvider(this));
- ClockManager clockManager = new ClockManager(this, new ContentProviderClockProvider(this),
- eventLogger);
+ ClockManager clockManager = new ClockManager(getContentResolver(),
+ new ContentProviderClockProvider(this), eventLogger);
if (clockManager.isAvailable()) {
mSections.put(R.id.nav_clock, new ClockSection(R.id.nav_clock, clockManager));
}
diff --git a/src/com/android/customization/picker/clock/ClockFragment.java b/src/com/android/customization/picker/clock/ClockFragment.java
index 8b795d4..87f6b20 100644
--- a/src/com/android/customization/picker/clock/ClockFragment.java
+++ b/src/com/android/customization/picker/clock/ClockFragment.java
@@ -28,6 +28,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.customization.model.CustomizationManager.Callback;
+import com.android.customization.model.clock.BaseClockManager;
import com.android.customization.model.clock.ClockManager;
import com.android.customization.model.clock.Clockface;
import com.android.customization.module.ThemesUserEventLogger;
@@ -49,7 +50,7 @@
* Interface to be implemented by an Activity hosting a {@link ClockFragment}
*/
public interface ClockFragmentHost {
- ClockManager getClockManager();
+ BaseClockManager getClockManager();
}
public static ClockFragment newInstance(CharSequence title) {
@@ -61,7 +62,7 @@
private RecyclerView mOptionsContainer;
private OptionSelectorController<Clockface> mOptionsController;
private Clockface mSelectedOption;
- private ClockManager mClockManager;
+ private BaseClockManager mClockManager;
private PreviewPager mPreviewPager;
private ThemesUserEventLogger mEventLogger;