Added settings UI for camera lift trigger gesture

Bug: 37154489
Test: manual + make RunSettingsRoboTests

- Added preference fragment for camera lift gesture
- Added activity alias for camera lift gesture setting
- Added resources + overlays for camera lift gesture setting
- Added config flag for limiting camera lift gesture setting for Pixel
- Added Robolectric tests for camera lift gesture settings classes

Change-Id: Ie4846cb1ea6b5279a19f20cb0299426f0d5cbb71
This commit is contained in:
Daniel Sheng
2017-04-18 19:50:37 -07:00
parent c73508b88d
commit 1721b802c5
15 changed files with 404 additions and 10 deletions

View File

@@ -3078,6 +3078,24 @@
android:permission="android.permission.DUMP" android:permission="android.permission.DUMP"
android:enabled="@bool/config_has_help" /> android:enabled="@bool/config_has_help" />
<!-- Activities for moves/gestures suggestions -->
<activity
android:name=".Settings$CameraLiftTriggerSuggestionActivity"
android:label="@string/camera_lift_trigger_title"
android:icon="@drawable/ic_settings_camera"
android:enabled="@bool/config_cameraLiftTriggerAvailable">
<intent-filter android:priority="1">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.suggested.category.GESTURE" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.gestures.CameraLiftTriggerSettings"/>
<meta-data android:name="com.android.settings.title"
android:resource="@string/camera_lift_trigger_title" />
<meta-data android:name="com.android.settings.summary"
android:resource="@string/camera_lift_trigger_suggestion_summary" />
</activity>
<!-- This is the longest AndroidManifest.xml ever. --> <!-- This is the longest AndroidManifest.xml ever. -->
</application> </application>
</manifest> </manifest>

View File

View File

View File

@@ -99,4 +99,7 @@
--> -->
</string-array> </string-array>
<!-- Whether or not the camera lift trigger is available in the moves menu. -->
<bool name="config_cameraLiftTriggerAvailable">false</bool>
</resources> </resources>

View File

@@ -8413,6 +8413,15 @@
<!-- Summary text for fingerprint swipe for notifications (device) [CHAR LIMIT=160]--> <!-- Summary text for fingerprint swipe for notifications (device) [CHAR LIMIT=160]-->
<string name="fingerprint_swipe_for_notifications_summary" product="device">To check your notifications, swipe down on the fingerprint sensor on the back of your device.</string> <string name="fingerprint_swipe_for_notifications_summary" product="device">To check your notifications, swipe down on the fingerprint sensor on the back of your device.</string>
<!-- Title and settings suggestion title text for opening camera when lifting the phone to a photo-taking position [CHAR LIMIT=60]-->
<string name="camera_lift_trigger_title">Lift to open camera</string>
<!-- Summary text for opening camera when lifting the phone to a photo-taking position [CHAR LIMIT=none]-->
<string name="camera_lift_trigger_summary">To open the camera automatically, lift up your phone into a photo-taking position (landscape or portrait). Works when your phone is asleep or locked.</string>
<!-- Settings suggestion summary text for opening camera when lifting the phone to a photo-taking position [CHAR LIMIT=60]-->
<string name="camera_lift_trigger_suggestion_summary">Never miss a moment</string>
<!-- Title text for the assist gesture [CHAR LIMIT=60]--> <!-- Title text for the assist gesture [CHAR LIMIT=60]-->
<string name="assist_gesture_title">Assist gesture</string> <string name="assist_gesture_title">Assist gesture</string>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/camera_lift_trigger_title">
<com.android.settings.widget.VideoPreference
android:key="gesture_camera_lift_trigger_video"
app:animation="@raw/gesture_camera_lift"
app:preview="@drawable/gesture_camera_lift"/>
<SwitchPreference
android:key="gesture_camera_lift_trigger"
android:title="@string/camera_lift_trigger_title"
android:summary="@string/camera_lift_trigger_summary"/>
</PreferenceScreen>

View File

@@ -75,16 +75,6 @@
android:title="@string/fingerprint_swipe_for_notifications_title" android:title="@string/fingerprint_swipe_for_notifications_title"
android:fragment="com.android.settings.gestures.SwipeToNotificationSettings"/> android:fragment="com.android.settings.gestures.SwipeToNotificationSettings"/>
<Preference
android:key="gesture_double_tap_power"
android:title="@string/double_tap_power_for_camera_title"
android:fragment="com.android.settings.gestures.DoubleTapPowerSettings"/>
<Preference
android:key="gesture_double_twist"
android:title="@string/double_twist_for_camera_mode_title"
android:fragment="com.android.settings.gestures.DoubleTwistGestureSettings"/>
<Preference <Preference
android:key="gesture_double_tap_screen" android:key="gesture_double_tap_screen"
android:title="@string/ambient_display_title" android:title="@string/ambient_display_title"
@@ -95,6 +85,21 @@
android:title="@string/ambient_display_pickup_title" android:title="@string/ambient_display_pickup_title"
android:fragment="com.android.settings.gestures.PickupGestureSettings"/> android:fragment="com.android.settings.gestures.PickupGestureSettings"/>
<Preference
android:key="gesture_camera_lift_trigger_summary"
android:title="@string/camera_lift_trigger_title"
android:fragment="com.android.settings.gestures.CameraLiftTriggerSettings"/>
<Preference
android:key="gesture_double_tap_power"
android:title="@string/double_tap_power_for_camera_title"
android:fragment="com.android.settings.gestures.DoubleTapPowerSettings"/>
<Preference
android:key="gesture_double_twist"
android:title="@string/double_twist_for_camera_mode_title"
android:fragment="com.android.settings.gestures.DoubleTwistGestureSettings"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory

View File

@@ -145,6 +145,7 @@ public class Settings extends SettingsActivity {
/* empty */ /* empty */
} }
public static class ScreenLockSuggestionActivity extends ChooseLockGeneric { /* empty */ } public static class ScreenLockSuggestionActivity extends ChooseLockGeneric { /* empty */ }
public static class CameraLiftTriggerSuggestionActivity extends SettingsActivity { /* empty */ }
public static class WallpaperSettingsActivity extends SettingsActivity { /* empty */ } public static class WallpaperSettingsActivity extends SettingsActivity { /* empty */ }
public static class ManagedProfileSettingsActivity extends SettingsActivity { /* empty */ } public static class ManagedProfileSettingsActivity extends SettingsActivity { /* empty */ }
public static class DeletionHelperActivity extends SettingsActivity { /* empty */ } public static class DeletionHelperActivity extends SettingsActivity { /* empty */ }

View File

@@ -30,8 +30,10 @@ import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.provider.Settings;
import com.android.ims.ImsManager; import com.android.ims.ImsManager;
import com.android.settings.Settings.CameraLiftTriggerSuggestionActivity;
import com.android.settings.Settings.FingerprintEnrollSuggestionActivity; import com.android.settings.Settings.FingerprintEnrollSuggestionActivity;
import com.android.settings.Settings.FingerprintSuggestionActivity; import com.android.settings.Settings.FingerprintSuggestionActivity;
import com.android.settings.Settings.ScreenLockSuggestionActivity; import com.android.settings.Settings.ScreenLockSuggestionActivity;
@@ -73,6 +75,8 @@ public class SuggestionsChecks {
return true; return true;
} }
return manager.hasEnrolledFingerprints(); return manager.hasEnrolledFingerprints();
} else if (className.equals(CameraLiftTriggerSuggestionActivity.class.getName())) {
return isCameraLiftTriggerEnabled();
} }
SuggestionFeatureProvider provider = SuggestionFeatureProvider provider =
@@ -134,6 +138,12 @@ public class SuggestionsChecks {
return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0; return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0;
} }
private boolean isCameraLiftTriggerEnabled() {
final int triggerEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, 0);
return triggerEnabled == 1;
}
private final IWallpaperManagerCallback mCallback = new IWallpaperManagerCallback.Stub() { private final IWallpaperManagerCallback mCallback = new IWallpaperManagerCallback.Stub() {
@Override @Override
public void onWallpaperChanged() throws RemoteException { public void onWallpaperChanged() throws RemoteException {

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.gestures;
import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
public class CameraLiftTriggerPreferenceController extends GesturePreferenceController {
private static final String PREF_KEY_VIDEO = "gesture_camera_lift_trigger_video";
private final String mCameraLiftTriggerKey;
public CameraLiftTriggerPreferenceController(Context context, Lifecycle lifecycle, String key) {
super(context, lifecycle);
mCameraLiftTriggerKey = key;
}
@Override
public boolean isAvailable() {
return mContext.getResources().getBoolean(
R.bool.config_cameraLiftTriggerAvailable);
}
@Override
protected String getVideoPrefKey() {
return PREF_KEY_VIDEO;
}
@Override
public String getPreferenceKey() {
return mCameraLiftTriggerKey;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean enabled = (boolean) newValue;
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, enabled ? 0 : 1);
return true;
}
@Override
protected boolean isSwitchPrefEnabled() {
final int triggerEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, 0);
return triggerEnabled == 1;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.gestures;
import android.content.Context;
import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CameraLiftTriggerSettings extends DashboardFragment {
private static final String TAG = "CameraLiftTrigger";
private static final String KEY = "gesture_camera_lift_trigger";
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.SETTINGS_GESTURE_CAMERA_LIFT_TRIGGER;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.camera_lift_trigger_settings;
}
@Override
protected List<PreferenceController> getPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getLifecycle());
}
private static List<PreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
final List<PreferenceController> controllers = new ArrayList<>();
controllers.add(new CameraLiftTriggerPreferenceController(context, lifecycle, KEY));
return controllers;
}
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.camera_lift_trigger_settings;
return Arrays.asList(sir);
}
@Override
public List<PreferenceController> getPreferenceControllers(Context context) {
return buildPreferenceControllers(context, null /* lifecycle */);
}
};
}

View File

@@ -37,6 +37,7 @@ import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader; import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.gestures.AssistGesturePreferenceController; import com.android.settings.gestures.AssistGesturePreferenceController;
import com.android.settings.gestures.CameraLiftTriggerPreferenceController;
import com.android.settings.gestures.DoubleTapPowerPreferenceController; import com.android.settings.gestures.DoubleTapPowerPreferenceController;
import com.android.settings.gestures.DoubleTapScreenPreferenceController; import com.android.settings.gestures.DoubleTapScreenPreferenceController;
import com.android.settings.gestures.DoubleTwistPreferenceController; import com.android.settings.gestures.DoubleTwistPreferenceController;
@@ -56,6 +57,8 @@ public class LanguageAndInputSettings extends DashboardFragment {
private static final String TAG = "LangAndInputSettings"; private static final String TAG = "LangAndInputSettings";
private static final String KEY_CAMERA_LIFT_TRIGGER = "gesture_camera_lift_trigger_summary";
private AmbientDisplayConfiguration mAmbientDisplayConfig; private AmbientDisplayConfiguration mAmbientDisplayConfig;
@Override @Override
@@ -108,6 +111,8 @@ public class LanguageAndInputSettings extends DashboardFragment {
context, lifecycle, mAmbientDisplayConfig, UserHandle.myUserId())); context, lifecycle, mAmbientDisplayConfig, UserHandle.myUserId()));
controllers.add(new DoubleTapScreenPreferenceController( controllers.add(new DoubleTapScreenPreferenceController(
context, lifecycle, mAmbientDisplayConfig, UserHandle.myUserId())); context, lifecycle, mAmbientDisplayConfig, UserHandle.myUserId()));
controllers.add(new CameraLiftTriggerPreferenceController(context, lifecycle,
KEY_CAMERA_LIFT_TRIGGER));
controllers.add(new DefaultAutofillPreferenceController(context)); controllers.add(new DefaultAutofillPreferenceController(context));
return controllers; return controllers;
} }

View File

@@ -57,6 +57,7 @@ import com.android.settings.gestures.DoubleTapScreenSettings;
import com.android.settings.gestures.DoubleTwistGestureSettings; import com.android.settings.gestures.DoubleTwistGestureSettings;
import com.android.settings.gestures.PickupGestureSettings; import com.android.settings.gestures.PickupGestureSettings;
import com.android.settings.gestures.SwipeToNotificationSettings; import com.android.settings.gestures.SwipeToNotificationSettings;
import com.android.settings.gestures.CameraLiftTriggerSettings;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment; import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment; import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import com.android.settings.inputmethod.VirtualKeyboardFragment; import com.android.settings.inputmethod.VirtualKeyboardFragment;
@@ -140,6 +141,8 @@ public final class SearchIndexableResources {
addIndex(DoubleTwistGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures); addIndex(DoubleTwistGestureSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_gestures);
addIndex(SwipeToNotificationSettings.class, NO_DATA_RES_ID, addIndex(SwipeToNotificationSettings.class, NO_DATA_RES_ID,
R.drawable.ic_settings_gestures); R.drawable.ic_settings_gestures);
addIndex(CameraLiftTriggerSettings.class, NO_DATA_RES_ID,
R.drawable.ic_settings_gestures);
addIndex(LanguageAndInputSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_language); addIndex(LanguageAndInputSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_language);
addIndex(LocationSettings.class, R.xml.location_settings, R.drawable.ic_settings_location); addIndex(LocationSettings.class, R.xml.location_settings, R.drawable.ic_settings_location);
addIndex(ScanningSettings.class, R.xml.location_scanning, R.drawable.ic_settings_location); addIndex(ScanningSettings.class, R.xml.location_scanning, R.drawable.ic_settings_location);

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.gestures;
import android.content.Context;
import android.provider.Settings;
import android.support.v7.preference.PreferenceScreen;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.R;
import com.android.settings.search2.InlineSwitchPayload;
import com.android.settings.search2.ResultPayload;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import static android.provider.Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class CameraLiftTriggerPreferenceControllerTest {
private static final String KEY_CAMERA_LIFT_TRIGGER = "gesture_camera_lift_trigger";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private CameraLiftTriggerPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new CameraLiftTriggerPreferenceController(mContext, null,
KEY_CAMERA_LIFT_TRIGGER);
}
@Test
public void isAvailable_configIsTrue_shouldReturnTrue() {
when(mContext.getResources().
getBoolean(R.bool.config_cameraLiftTriggerAvailable))
.thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_configIsTrue_shouldReturnFalse() {
when(mContext.getResources().
getBoolean(R.bool.config_cameraLiftTriggerAvailable))
.thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void testSwitchEnabled_configIsNotSet_shouldReturnFalse() {
// Set the setting to be enabled.
final Context context = RuntimeEnvironment.application;
Settings.System.putInt(context.getContentResolver(),
CAMERA_LIFT_TRIGGER_ENABLED, 0);
mController = new CameraLiftTriggerPreferenceController(context, null,
KEY_CAMERA_LIFT_TRIGGER);
assertThat(mController.isSwitchPrefEnabled()).isFalse();
}
@Test
public void testSwitchEnabled_configIsSet_shouldReturnTrue() {
// Set the setting to be disabled.
final Context context = RuntimeEnvironment.application;
Settings.System.putInt(context.getContentResolver(),
CAMERA_LIFT_TRIGGER_ENABLED, 1);
mController = new CameraLiftTriggerPreferenceController(context, null,
KEY_CAMERA_LIFT_TRIGGER);
assertThat(mController.isSwitchPrefEnabled()).isTrue();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.gestures;
import android.provider.SearchIndexableResource;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class CameraLiftTriggerSettingsTest {
private CameraLiftTriggerSettings mSettings;
@Before
public void setUp() {
mSettings = new CameraLiftTriggerSettings();
}
@Test
public void testSearchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes =
CameraLiftTriggerSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(
ShadowApplication.getInstance().getApplicationContext(),
true /* enabled */);
assertThat(indexRes).isNotNull();
assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId());
}
}