Merge "Add activities to support voice settings."
This commit is contained in:
committed by
Android (Google) Code Review
commit
dfb369d43f
@@ -158,6 +158,17 @@
|
||||
android:value="true" />
|
||||
</activity>
|
||||
|
||||
<activity android:name="AirplaneModeVoiceActivity"
|
||||
android:label="@string/wireless_networks_settings_title"
|
||||
android:theme="@android:style/Theme.Material.Light.Voice"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.VOICE_CONTROL_AIRPLANE_MODE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.VOICE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Top-level settings -->
|
||||
|
||||
<activity android:name="Settings$WifiSettingsActivity"
|
||||
@@ -996,6 +1007,16 @@
|
||||
android:value="com.android.settings.notification.NotificationStation" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".notification.ZenModeVoiceActivity"
|
||||
android:theme="@android:style/Theme.Material.Light"
|
||||
android:label="@string/zen_mode_settings_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.VOICE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
<activity android:name="Settings$AppOpsSummaryActivity"
|
||||
android:label="@string/app_ops_settings"
|
||||
@@ -1874,6 +1895,17 @@
|
||||
android:value="true" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".fuelguage.BatterySaverModeVoiceActivity"
|
||||
android:label="@string/power_usage_summary_title"
|
||||
android:theme="@android:style/Theme.Material.Light.Voice"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.VOICE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="Settings$AccountSettingsActivity"
|
||||
android:label="@string/account_settings_title"
|
||||
android:taskAffinity=""
|
||||
|
21
res/drawable/bg_circle_blue.xml
Normal file
21
res/drawable/bg_circle_blue.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/blue" />
|
||||
</shape>
|
23
res/layout/voice_interaction.xml
Normal file
23
res/layout/voice_interaction.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 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"
|
||||
android:id="@+id/fragment_root"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</FrameLayout>
|
44
res/layout/voice_item_row.xml
Normal file
44
res/layout/voice_item_row.xml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
<LinearLayout
|
||||
android:id="@+id/row_one"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0px"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:id="@+id/voice_item_label" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="100px"
|
||||
android:layout_height="100px"
|
||||
android:gravity="center_horizontal|center_vertical"
|
||||
android:background="@drawable/bg_circle_blue"
|
||||
android:textAppearance="?android:attr/textAppearanceMediumInverse"
|
||||
android:textStyle="bold"
|
||||
android:id="@+id/voice_item_position" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@@ -17,6 +17,7 @@
|
||||
<resources>
|
||||
<color name="black">#000</color>
|
||||
<color name="red">#F00</color>
|
||||
<color name="blue">#00F</color>
|
||||
|
||||
<color name="material_empty_color_light">#FFCED7DB</color>
|
||||
|
||||
|
@@ -5991,6 +5991,93 @@
|
||||
<!-- [CHAR LIMIT=60] Zen mode settings: End time option: Summary text value format when end time = next day -->
|
||||
<string name="zen_mode_end_time_next_day_summary_format"><xliff:g id="formatted_time">%s</xliff:g> next day</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Prompt read for interruption type -->
|
||||
<string name="zen_mode_interruptions_voice_prompt">When would you like to be interrupted?</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Prompt read for zen mode duration -->
|
||||
<string name="zen_mode_duration_voice_prompt">For how long?</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Comma delimited synonyms for important interriuptions -->
|
||||
<string name="zen_mode_option_important_voice_synonyms">important,priority,priority notifications</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Comma delimited synonyms for alarm interriuptions -->
|
||||
<string name="zen_mode_option_alarms_voice_synonyms">alarms</string>
|
||||
|
||||
<!-- [CHAR LIMIT=60] Zen mode voice: Off [CHAR LIMIT=60] -->
|
||||
<string name="zen_mode_option_off">Off</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Comma delimited synonyms for off interriuptions -->
|
||||
<string name="zen_mode_option_off_voice_synonyms">off,all,everything</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Comma delimited synonyms for no interriuptions -->
|
||||
<string name="zen_mode_option_no_interruptions_voice_synonyms">none,nothing,no interruptions</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode voice: Label for indefinite mode duration -->
|
||||
<string name="zen_mode_duration_indefinte_voice_label">Indefinitely</string>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode voice: Label for duration in minutes -->
|
||||
<plurals name="zen_mode_duration_minutes_voice_label">
|
||||
<item quantity="one"><xliff:g id="count" example="1">%d</xliff:g> minute</item>
|
||||
<item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> minutes</item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=40] Zen mode voice: Label for duration in hours -->
|
||||
<plurals name="zen_mode_duration_hours_voice_label">
|
||||
<item quantity="one"><xliff:g id="count" example="1">%d</xliff:g> hour</item>
|
||||
<item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> hours</item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice - spoken summary: important only duration indefinite. -->
|
||||
<string name="zen_mode_summary_priority_indefinitely">Change to priority notifications only indefinitely</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: important only duration minutes. -->
|
||||
<plurals name="zen_mode_summary_priority_by_minute">
|
||||
<item quantity="one">Change to priority notifications only for one minute until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to priority notifications only for <xliff:g id="duration" example="2">%1$d</xliff:g> minutes until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: important only duration hours. -->
|
||||
<plurals name="zen_mode_summary_priority_by_hour">
|
||||
<item quantity="one">Change to priority notifications only for one hour until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to priority notifications only for <xliff:g id="duration" example="2">%1$d</xliff:g> hours (until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g>)</item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice - spoken summary: alarms only duration indefinite. -->
|
||||
<string name="zen_mode_summary_alarams_only_indefinite">Change to alarms only indefinitely</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: alarms only duration minutes. -->
|
||||
<plurals name="zen_mode_summary_alarms_only_by_minute">
|
||||
<item quantity="one">Change to alarms only for one minute until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to alarms only for <xliff:g id="duration" example="2">%1$d</xliff:g> minutes (until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g>)</item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: alarms only duration hours. -->
|
||||
<plurals name="zen_mode_summary_alarms_only_by_hour">
|
||||
<item quantity="one">Change to alarms only for one hour until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to alarms only for <xliff:g id="duration" example="2">%1$d</xliff:g> hours until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice - spoken summary: no interruptions duration indefinite. -->
|
||||
<string name="zen_mode_summary_no_interruptions_indefinite">Change to don\'t interrupt indefinitely</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: alarms only duration minutes. -->
|
||||
<plurals name="zen_mode_summary_no_interruptions_by_minute">
|
||||
<item quantity="one">Change to don\'t interrupt for one minute until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to don\'t interrupt for <xliff:g id="duration" example="2">%1$d</xliff:g> minutes (until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g>)</item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice- spoken summary: alarms only duration hours. -->
|
||||
<plurals name="zen_mode_summary_no_interruptions_by_hour">
|
||||
<item quantity="one">Change to don\'t interrupt for one hour until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
<item quantity="other">Change to don\'t interrupt for <xliff:g id="duration" example="2">%1$d</xliff:g> hours until <xliff:g id="formattedTime" example="10:00 PM">%2$s</xliff:g></item>
|
||||
</plurals>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice - spoken summary: off. -->
|
||||
<string name="zen_mode_summary_always">Change to always interrupt</string>
|
||||
|
||||
<!-- [CHAR LIMIT=NONE] Zen mode voice: Comma delimited synonyms for indefinte duration -->
|
||||
<string name="zen_mode_duration_indefinite_voice_synonyms">forever</string>
|
||||
|
||||
<!-- [CHAR LIMIT=20] Notifications settings: Apps section header -->
|
||||
<string name="notification_settings_apps_title">App notifications</string>
|
||||
|
||||
|
@@ -20,6 +20,8 @@ import android.content.Intent;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.utils.VoiceSettingsActivity;
|
||||
|
||||
/**
|
||||
* Activity for modifying the {@link Settings.Global#AIRPLANE_MODE_ON AIRPLANE_MODE_ON}
|
||||
* setting using the Voice Interaction API.
|
||||
@@ -27,14 +29,14 @@ import android.util.Log;
|
||||
public class AirplaneModeVoiceActivity extends VoiceSettingsActivity {
|
||||
private static final String TAG = "AirplaneModeVoiceActivity";
|
||||
|
||||
protected void onVoiceSettingInteraction(Intent intent) {
|
||||
protected boolean onVoiceSettingInteraction(Intent intent) {
|
||||
if (intent.hasExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED)) {
|
||||
boolean enabled =
|
||||
intent.getBooleanExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED, false);
|
||||
Settings.Global.putInt(getContentResolver(),
|
||||
Settings.Global.AIRPLANE_MODE_ON, enabled ? 1 : 0);
|
||||
Settings.Global.AIRPLANE_MODE_ON,
|
||||
intent.getBooleanExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED, false) ? 1 : 0);
|
||||
} else {
|
||||
Log.v(TAG, "Missing airplane mode extra");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.fuelguage;
|
||||
|
||||
import static android.provider.Settings.EXTRA_BATTERY_SAVER_MODE_ENABLED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.utils.VoiceSettingsActivity;
|
||||
|
||||
/**
|
||||
* Activity for modifying the {@link android.os.PowerManager} power save mode
|
||||
* setting using the Voice Interaction API.
|
||||
*/
|
||||
public class BatterySaverModeVoiceActivity extends VoiceSettingsActivity {
|
||||
private static final String TAG = "BatterySaverModeVoiceActivity";
|
||||
|
||||
protected boolean onVoiceSettingInteraction(Intent intent) {
|
||||
if (intent.hasExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED)) {
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
if (powerManager.setPowerSaveMode(
|
||||
intent.getBooleanExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED, false))) {
|
||||
notifySuccess(null);
|
||||
} else {
|
||||
Log.v(TAG, "Unable to set power mode");
|
||||
notifyFailure(null);
|
||||
}
|
||||
} else {
|
||||
Log.v(TAG, "Missing battery saver mode extra");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
241
src/com/android/settings/notification/ZenModeVoiceActivity.java
Normal file
241
src/com/android/settings/notification/ZenModeVoiceActivity.java
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.notification;
|
||||
|
||||
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
|
||||
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.utils.VoiceSelectionAdapter;
|
||||
import com.android.settings.utils.VoiceSelection;
|
||||
import com.android.settings.utils.VoiceSelectionFragment;
|
||||
import com.android.settings.utils.VoiceSettingsActivity;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Global;
|
||||
import android.service.notification.Condition;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Activity for modifying the Zen mode (Do not disturb) by voice
|
||||
* using the Voice Interaction API.
|
||||
*/
|
||||
public class ZenModeVoiceActivity extends VoiceSettingsActivity {
|
||||
private static final String TAG = "ZenModeVoiceActivity";
|
||||
private static final int MINUTES_MS = 60 * 1000;
|
||||
|
||||
@Override
|
||||
protected boolean onVoiceSettingInteraction(Intent intent) {
|
||||
setContentView(R.layout.voice_interaction);
|
||||
pickNotificationMode(intent);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a voice interaction to ask what kind of interruptions should
|
||||
* be permitted. The intent can optionally include extra information about the type
|
||||
* of interruptions desired or how long interruptions should be limited to that are
|
||||
* used as hints.
|
||||
*/
|
||||
private void pickNotificationMode(final Intent intent) {
|
||||
boolean enabled = intent.getBooleanExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED, false);
|
||||
boolean specified = intent.hasExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED);
|
||||
|
||||
List<VoiceSelection> states = new ArrayList<VoiceSelection>();
|
||||
if (!specified || enabled) {
|
||||
states.add(new ModeSelection(this, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
|
||||
R.string.zen_mode_option_important_interruptions,
|
||||
R.string.zen_mode_option_important_voice_synonyms));
|
||||
states.add(new ModeSelection(this, Global.ZEN_MODE_ALARMS,
|
||||
R.string.zen_mode_option_alarms,
|
||||
R.string.zen_mode_option_alarms_voice_synonyms));
|
||||
states.add(new ModeSelection(this, Global.ZEN_MODE_NO_INTERRUPTIONS,
|
||||
R.string.zen_mode_option_no_interruptions,
|
||||
R.string.zen_mode_option_no_interruptions_voice_synonyms));
|
||||
}
|
||||
if (!specified || !enabled) {
|
||||
states.add(new ModeSelection(this, Global.ZEN_MODE_OFF,
|
||||
R.string.zen_mode_option_off,
|
||||
R.string.zen_mode_option_off_voice_synonyms));
|
||||
}
|
||||
VoiceSelectionFragment fragment = new VoiceSelectionFragment();
|
||||
fragment.setArguments(VoiceSelectionFragment.createArguments(
|
||||
getString(R.string.zen_mode_interruptions_voice_prompt)));
|
||||
fragment.setListAdapter(
|
||||
new VoiceSelectionAdapter(this, R.layout.voice_item_row, states));
|
||||
fragment.setOnItemSelectedHandler(new VoiceSelection.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(int index, VoiceSelection selection) {
|
||||
int mode = ((ModeSelection) selection).mMode;
|
||||
ConditionSelection conditionSelection = getConditionSelection(
|
||||
intent.getIntExtra(EXTRA_DO_NOT_DISTURB_MODE_MINUTES, 0));
|
||||
if (mode != Global.ZEN_MODE_OFF) {
|
||||
if (conditionSelection == null) {
|
||||
pickDuration(selection.getLabel(), mode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setZenModeConfig(mode, conditionSelection.mCondition);
|
||||
notifySuccess(getChangeSummary(mode, conditionSelection));
|
||||
finish();
|
||||
}
|
||||
});
|
||||
showFragment(fragment, "pick_mode_fragment");
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a voice interaction to ask for the zen mode duration.
|
||||
*/
|
||||
private void pickDuration(CharSequence label, final int mode) {
|
||||
setTitle(label.toString());
|
||||
List<VoiceSelection> states = new ArrayList<VoiceSelection>();
|
||||
states.add(new ConditionSelection(null, -1,
|
||||
getString(R.string.zen_mode_duration_indefinte_voice_label),
|
||||
getString(R.string.zen_mode_duration_indefinite_voice_synonyms)));
|
||||
for (int i = ZenModeConfig.MINUTE_BUCKETS.length - 1; i >= 0; --i) {
|
||||
states.add(getConditionSelection(ZenModeConfig.MINUTE_BUCKETS[i]));
|
||||
}
|
||||
|
||||
VoiceSelectionFragment fragment = new VoiceSelectionFragment();
|
||||
fragment.setArguments(VoiceSelectionFragment.createArguments(
|
||||
getString(R.string.zen_mode_duration_voice_prompt)));
|
||||
fragment.setListAdapter(
|
||||
new VoiceSelectionAdapter(this, R.layout.voice_item_row, states));
|
||||
fragment.setOnItemSelectedHandler(new VoiceSelection.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(int index, VoiceSelection item) {
|
||||
ConditionSelection selection = ((ConditionSelection) item);
|
||||
setZenModeConfig(mode, selection.mCondition);
|
||||
notifySuccess(getChangeSummary(mode, selection));
|
||||
finish();
|
||||
}
|
||||
});
|
||||
showFragment(fragment, "pick_duration_fragment");
|
||||
}
|
||||
|
||||
private void showFragment(Fragment fragment, String tag) {
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_root, fragment, tag)
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void setZenModeConfig(int mode, Condition condition) {
|
||||
if (condition != null) {
|
||||
NotificationManager.from(this).setZenMode(mode, condition.id, TAG);
|
||||
} else {
|
||||
NotificationManager.from(this).setZenMode(mode, null, TAG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a summary of the Zen mode change to be read aloud as TTS.
|
||||
*/
|
||||
private CharSequence getChangeSummary(int mode, ConditionSelection duration) {
|
||||
int indefinite = -1;
|
||||
int byMinute = -1;
|
||||
int byHour = -1;
|
||||
|
||||
switch (mode) {
|
||||
case Global.ZEN_MODE_ALARMS:
|
||||
indefinite = R.string.zen_mode_summary_alarams_only_indefinite;
|
||||
byMinute = R.plurals.zen_mode_summary_alarms_only_by_minute;
|
||||
byHour = R.plurals.zen_mode_summary_alarms_only_by_hour;
|
||||
break;
|
||||
case Global.ZEN_MODE_NO_INTERRUPTIONS:
|
||||
indefinite = R.string.zen_mode_summary_no_interruptions_indefinite;
|
||||
byMinute = R.plurals.zen_mode_summary_no_interruptions_by_minute;
|
||||
byHour = R.plurals.zen_mode_summary_no_interruptions_by_hour;
|
||||
break;
|
||||
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
|
||||
indefinite = R.string.zen_mode_summary_priority_indefinitely;
|
||||
byMinute = R.plurals.zen_mode_summary_priority_by_minute;
|
||||
byHour = R.plurals.zen_mode_summary_priority_by_hour;
|
||||
break;
|
||||
default:
|
||||
case Global.ZEN_MODE_OFF:
|
||||
indefinite = R.string.zen_mode_summary_always;
|
||||
break;
|
||||
};
|
||||
|
||||
if (duration == null || duration.mCondition == null) {
|
||||
return getString(indefinite);
|
||||
}
|
||||
|
||||
long time = System.currentTimeMillis() + duration.mMinutes * MINUTES_MS;
|
||||
String skeleton = DateFormat.is24HourFormat(this, UserHandle.myUserId()) ? "Hm" : "hma";
|
||||
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
|
||||
CharSequence formattedTime = DateFormat.format(pattern, time);
|
||||
Resources res = getResources();
|
||||
|
||||
if (duration.mMinutes < 60) {
|
||||
return res.getQuantityString(byMinute,
|
||||
duration.mMinutes, duration.mMinutes, formattedTime);
|
||||
} else {
|
||||
int hours = duration.mMinutes / 60;
|
||||
return res.getQuantityString(byHour, hours, hours, formattedTime);
|
||||
}
|
||||
}
|
||||
|
||||
private ConditionSelection getConditionSelection(int minutes) {
|
||||
Condition condition = ZenModeConfig.toTimeCondition(this, minutes, UserHandle.myUserId());
|
||||
Resources res = getResources();
|
||||
if (minutes <= 0) {
|
||||
return null;
|
||||
} else if (minutes < 60) {
|
||||
String label = res.getQuantityString(R.plurals.zen_mode_duration_minutes_voice_label,
|
||||
minutes, minutes);
|
||||
return new ConditionSelection(condition, minutes, label, Integer.toString(minutes));
|
||||
} else {
|
||||
int hours = minutes / 60;
|
||||
String label = res.getQuantityString(R.plurals.zen_mode_duration_hours_voice_label,
|
||||
hours, hours);
|
||||
return new ConditionSelection(condition, minutes, label, Integer.toString(hours));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConditionSelection extends VoiceSelection {
|
||||
Condition mCondition;
|
||||
int mMinutes;
|
||||
|
||||
public ConditionSelection(Condition condition, int minutes, CharSequence label,
|
||||
CharSequence synonyms) {
|
||||
super(label, synonyms);
|
||||
mMinutes = minutes;
|
||||
mCondition = condition;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModeSelection extends VoiceSelection {
|
||||
int mMode;
|
||||
|
||||
public ModeSelection(Context context, int mode, int label, int synonyms) {
|
||||
super(context.getString(label), context.getString(synonyms));
|
||||
mMode = mode;
|
||||
}
|
||||
}
|
||||
}
|
74
src/com/android/settings/utils/VoiceSelection.java
Normal file
74
src/com/android/settings/utils/VoiceSelection.java
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.utils;
|
||||
|
||||
import android.app.VoiceInteractor.PickOptionRequest.Option;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Model for a single item that can be selected by a {@link VoiceSelectionFragment}.
|
||||
* Each item consists of a visual label and several alternative synonyms for the item
|
||||
* that can be used to identify the item by voice.
|
||||
*/
|
||||
public class VoiceSelection {
|
||||
final CharSequence mLabel;
|
||||
final CharSequence[] mSynonyms;
|
||||
|
||||
/**
|
||||
* Created a new selectable item with a visual label and a set of synonyms.
|
||||
*/
|
||||
public VoiceSelection(CharSequence label, CharSequence synonyms) {
|
||||
mLabel = label;
|
||||
mSynonyms = TextUtils.split(synonyms.toString(), ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* Created a new selectable item with a visual label and no synonyms.
|
||||
*/
|
||||
public VoiceSelection(CharSequence label) {
|
||||
mLabel = label;
|
||||
mSynonyms = null;
|
||||
}
|
||||
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public CharSequence[] getSynonyms() {
|
||||
return mSynonyms;
|
||||
}
|
||||
|
||||
Option toOption(int index) {
|
||||
Option result = new Option(mLabel);
|
||||
Bundle extras = new Bundle();
|
||||
extras.putInt("index", index);
|
||||
result.setExtras(extras);
|
||||
|
||||
for (CharSequence synonym : mSynonyms) {
|
||||
result.addSynonym(synonym);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface for when an item is selected.
|
||||
*/
|
||||
public interface OnItemSelectedListener {
|
||||
abstract void onItemSelected(int position, VoiceSelection selection);
|
||||
};
|
||||
}
|
63
src/com/android/settings/utils/VoiceSelectionAdapter.java
Normal file
63
src/com/android/settings/utils/VoiceSelectionAdapter.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.app.Activity;
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.List;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Array adapter for selecting an item by voice interaction. Each row includes a visual
|
||||
* indication of the 1-indexed position of the item so that a user can easily say
|
||||
* "number 4" to select it.
|
||||
*/
|
||||
public class VoiceSelectionAdapter extends ArrayAdapter<VoiceSelection> {
|
||||
public VoiceSelectionAdapter(Context context, int resource, List<VoiceSelection> objects) {
|
||||
super(context, resource, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
VoiceSelection item = getItem(position);
|
||||
View row = convertView;
|
||||
if (row == null) {
|
||||
LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
|
||||
row = inflater.inflate(R.layout.voice_item_row, parent, false);
|
||||
}
|
||||
|
||||
TextView label = (TextView) row.findViewById(R.id.voice_item_label);
|
||||
if (label != null) {
|
||||
label.setText(item.getLabel());
|
||||
}
|
||||
|
||||
TextView positionLabel = (TextView) row.findViewById(R.id.voice_item_position);
|
||||
if (positionLabel != null) {
|
||||
positionLabel.setText(Integer.toString(position + 1));
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
};
|
133
src/com/android/settings/utils/VoiceSelectionFragment.java
Normal file
133
src/com/android/settings/utils/VoiceSelectionFragment.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.utils;
|
||||
|
||||
import android.app.ListFragment;
|
||||
import android.app.VoiceInteractor;
|
||||
import android.app.VoiceInteractor.PickOptionRequest;
|
||||
import android.app.VoiceInteractor.PickOptionRequest.Option;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An Activity fragment that presents a set of options as a visual list and also allows
|
||||
* items to be selected by the users voice.
|
||||
*/
|
||||
public class VoiceSelectionFragment extends ListFragment {
|
||||
private static final String EXTRA_SELECTION_PROMPT = "selection_prompt";
|
||||
|
||||
private CharSequence mPrompt = null;
|
||||
private VoiceInteractor.Request mRequest = null;
|
||||
private VoiceInteractor mVoiceInteractor = null;
|
||||
private VoiceSelection.OnItemSelectedListener mOnItemSelectedListener = null;
|
||||
|
||||
/**
|
||||
* No-args ctor required for fragment.
|
||||
*/
|
||||
public VoiceSelectionFragment() {}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle args) {
|
||||
super.onCreate(args);
|
||||
mPrompt = getArguments().getCharSequence(EXTRA_SELECTION_PROMPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prompt spoken when the fragment is presented.
|
||||
*/
|
||||
static public Bundle createArguments(CharSequence prompt) {
|
||||
Bundle args = new Bundle();
|
||||
args.putCharSequence(EXTRA_SELECTION_PROMPT, prompt);
|
||||
return args;
|
||||
}
|
||||
|
||||
private VoiceSelection getSelectionAt(int position) {
|
||||
return ((ArrayAdapter<VoiceSelection>) getListAdapter()).getItem(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
final int numItems = getListAdapter().getCount();
|
||||
if (numItems <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Option[] options = new Option[numItems];
|
||||
for (int idx = 0; idx < numItems; idx++) {
|
||||
options[idx] = getSelectionAt(idx).toOption(idx);
|
||||
}
|
||||
mRequest = new PickOptionRequest(mPrompt, options, null) {
|
||||
@Override
|
||||
public void onPickOptionResult(boolean isComplete, Option[] options, Bundle args) {
|
||||
if (!isComplete || options == null) {
|
||||
return;
|
||||
}
|
||||
if (options.length == 1 && mOnItemSelectedListener != null) {
|
||||
int idx = options[0].getExtras().getInt("index", -1);
|
||||
mOnItemSelectedListener.onItemSelected(idx, getSelectionAt(idx));
|
||||
} else {
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
mVoiceInteractor = getActivity().getVoiceInteractor();
|
||||
if (mVoiceInteractor != null) {
|
||||
mVoiceInteractor.submitRequest(mRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mVoiceInteractor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
if (mRequest != null) {
|
||||
mRequest.cancel();
|
||||
mRequest = null;
|
||||
}
|
||||
|
||||
if (mOnItemSelectedListener != null) {
|
||||
mOnItemSelectedListener.onItemSelected(position, getSelectionAt(position));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the selection handler for an item either by voice or by touch.
|
||||
*/
|
||||
public void setOnItemSelectedHandler(VoiceSelection.OnItemSelectedListener listener) {
|
||||
mOnItemSelectedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user cancels the interaction. The default implementation is to
|
||||
* finish the activity.
|
||||
*/
|
||||
public void onCancel() {
|
||||
getActivity().finish();
|
||||
}
|
||||
};
|
@@ -14,17 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings;
|
||||
package com.android.settings.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.VoiceInteractor;
|
||||
import android.app.VoiceInteractor.CompleteVoiceRequest;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Activity for modifying a setting using the Voice Interaction API. This activity
|
||||
* MUST only modify the setting if the intent was sent using
|
||||
* {@link android.service.voice.VoiceInteractionSession#startVoiceActivity startVoiceActivity}.
|
||||
* will only allow modifying the setting if the intent was sent using
|
||||
* {@link android.service.voice.VoiceInteractionSession#startVoiceActivity startVoiceActivity}
|
||||
* by the current Voice Interaction Service.
|
||||
*/
|
||||
abstract public class VoiceSettingsActivity extends Activity {
|
||||
|
||||
@@ -34,18 +37,38 @@ abstract public class VoiceSettingsActivity extends Activity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (isVoiceInteraction()) {
|
||||
if (isVoiceInteraction() || savedInstanceState == null) {
|
||||
// Only permit if this is a voice interaction.
|
||||
onVoiceSettingInteraction(getIntent());
|
||||
if (onVoiceSettingInteraction(getIntent())) {
|
||||
// If it's complete, finish.
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
Log.v(TAG, "Cannot modify settings without voice interaction");
|
||||
finish();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the setting as a voice interaction. The activity will finish
|
||||
* after this method is called.
|
||||
* Modify the setting as a voice interaction. Should return true if the
|
||||
* voice interaction is complete or false if more interaction is required.
|
||||
*/
|
||||
abstract protected void onVoiceSettingInteraction(Intent intent);
|
||||
abstract protected boolean onVoiceSettingInteraction(Intent intent);
|
||||
|
||||
/**
|
||||
* Send a notification that the interaction was successful. If {@link prompt} is
|
||||
* not null, then it will be read to the user.
|
||||
*/
|
||||
protected void notifySuccess(CharSequence prompt) {
|
||||
if (getVoiceInteractor() != null) {
|
||||
getVoiceInteractor().submitRequest(new CompleteVoiceRequest(prompt, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates when the setting could not be changed.
|
||||
*/
|
||||
protected void notifyFailure(String reason) {
|
||||
getVoiceInteractor().submitRequest(new VoiceInteractor.AbortVoiceRequest(reason, null));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user