make quick setting distraction optimized
Bug: 76805265
Test: manual
Change-Id: I0e1336c75ecb47a14a550c8bf0fe6a4880c2883c
diff --git a/Android.mk b/Android.mk
index 99a4003..d9a7784 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,7 +22,7 @@
# (for example, projected). See b/30064991
ifeq (,$(TARGET_BUILD_APPS))
LOCAL_PACKAGE_NAME := CarSettings
-LOCAL_PRIVATE_PLATFORM_APIS := true
+ LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 20006e2..01aa181 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -82,6 +82,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ <meta-data android:name="distractionOptimized" android:value="true"/>
</activity>
<activity android:name=".bluetooth.BluetoothPairingDialog"
diff --git a/res/drawable/circle_bg.xml b/res/drawable/circle_bg.xml
new file mode 100644
index 0000000..b002eba
--- /dev/null
+++ b/res/drawable/circle_bg.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid
+ android:color="@color/car_accent"/>
+
+ <size
+ android:width="@dimen/car_touch_target_size"
+ android:height="@dimen/car_touch_target_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/layout/action_bar_quick_settings.xml b/res/layout/action_bar_quick_settings.xml
new file mode 100644
index 0000000..88856dd
--- /dev/null
+++ b/res/layout/action_bar_quick_settings.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_app_bar_height">
+
+ <FrameLayout
+ android:id="@+id/action_bar_icon_container"
+ android:layout_width="@dimen/car_margin"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true">
+
+ <ImageView
+ android:id="@+id/exit_button"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_close"
+ style="@style/ListIcon.ActionBar"/>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/car_keyline_1"
+ android:layout_toEndOf="@id/action_bar_icon_container"
+ android:text="@string/settings_label"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.Car.Title2"
+ android:textColor="@color/car_accent"/>
+
+ <LinearLayout
+ android:id="@+id/button_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="@dimen/car_keyline_1"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/user_switcher_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/car_padding_4"
+ style="@style/Widget.Car.Settings.ActionBar.Button.Borderless.Colored"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/user_icon"
+ android:src="@drawable/ic_user"
+ style="@style/ListIcon"/>
+ <TextView
+ android:id="@+id/user_switcher_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.Car.Label1"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/full_setting_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/car_padding_4"
+ style="@style/Widget.Car.Settings.ActionBar.Button.Borderless.Colored"
+ android:orientation="horizontal">
+ <ImageView
+ android:src="@drawable/ic_settings_gear"
+ style="@style/ListIcon"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/more_settings_label"
+ android:textAppearance="@style/TextAppearance.Car.Label1"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"/>
+ </LinearLayout>
+ </LinearLayout>
+ <View
+ android:layout_alignParentBottom="true"
+ android:background="@color/car_list_divider"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_list_divider_height" />
+</RelativeLayout>
diff --git a/res/layout/tile.xml b/res/layout/tile.xml
index 9f1ebcc..58e3b88 100644
--- a/res/layout/tile.xml
+++ b/res/layout/tile.xml
@@ -18,26 +18,35 @@
<com.android.car.list.InterceptTouchRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_container"
- android:foreground="@drawable/car_list_item_background"
+ android:padding="@dimen/toggle_padding"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"
+ android:layout_marginBottom="@dimen/car_padding_5"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <ImageButton
- android:id="@+id/tile_icon"
+ <FrameLayout
+ android:id="@+id/icon_container"
android:layout_width="@dimen/car_touch_target_size"
android:layout_height="@dimen/car_touch_target_size"
- android:layout_marginBottom="@dimen/car_padding_1"
- android:layout_alignParentTop="true"
+ android:background="@drawable/circle_bg"
android:layout_centerHorizontal="true"
- android:src="@drawable/ic_settings_wifi"
- android:tint="@color/car_label1"
- android:background="@null"
- style="@style/ListIcon" />
+ android:layout_marginBottom="@dimen/car_padding_4">
+ <ImageButton
+ android:id="@+id/tile_icon"
+ android:layout_width="@dimen/car_primary_icon_size"
+ android:layout_height="@dimen/car_primary_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_settings_wifi"
+ android:tint="@color/car_card"
+ android:background="@null"
+ style="@style/ListIcon" />
+ </FrameLayout>
<TextView
android:id="@+id/tile_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingTop="@dimen/car_padding_1"
android:layout_centerHorizontal="true"
- android:layout_below="@+id/tile_icon"
+ android:layout_below="@id/icon_container"
android:textAppearance="@style/TextAppearance.Car.Label1"/>
</com.android.car.list.InterceptTouchRelativeLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index cfc2d1d..23caf72 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -38,5 +38,5 @@
<dimen name="pin_pad_key_text_size">48sp</dimen>
<dimen name="lockscreen_title_drawable_padding">5dp</dimen>
-
+ <dimen name="toggle_padding">10dp</dimen>
</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0c2cc41..a35f667 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -19,7 +19,7 @@
<!-- Application name, temporarily misspelled to allow it to live alongside the regular
android settings without confusion. -->
<string name="settings_label">Settings</string>
- <string name="advanced_settings_label">More settings</string>
+ <string name="more_settings_label">More</string>
<string name="display_settings">Display</string>
<!-- Sound & display settings screen, setting option name to change brightness level -->
<string name="brightness">Brightness level</string>
@@ -590,4 +590,8 @@
<string name="suggestion_primary_button">finish setup</string>
<!-- Button that dismisses the suggestion to finish setting up their device (changed to all caps so case doesn't matter) [CHAR_LIMIT=30] -->
<string name="suggestion_secondary_button">not now</string>
+
+ <!-- Warn user that the action they are trying to perform is blocked while the car is in
+ motion [CHAR LIMIT=60] -->
+ <string name="restricted_while_driving">Action not available while driving.</string>
</resources>
diff --git a/src/com/android/car/settings/common/CarUxRestrictionsHelper.java b/src/com/android/car/settings/common/CarUxRestrictionsHelper.java
new file mode 100644
index 0000000..492367a
--- /dev/null
+++ b/src/com/android/car/settings/common/CarUxRestrictionsHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.settings.common;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Class that helps registering {@link CarUxRestrictionsManager.onUxRestrictionsChangedListener} and
+ * managing car connection.
+ */
+public class CarUxRestrictionsHelper {
+ private static final String TAG = "CarUxRestrictionsHelper";
+
+ // mCar is created in the constructor, but can be null if connection to the car is not
+ // successful.
+ @Nullable private final Car mCar;
+ @Nullable private CarUxRestrictionsManager mCarUxRestrictionsManager;
+
+ private final CarUxRestrictionsManager.onUxRestrictionsChangedListener mListener;
+
+ public CarUxRestrictionsHelper(Context context,
+ CarUxRestrictionsManager.onUxRestrictionsChangedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener cannot be null.");
+ }
+ mListener = listener;
+ mCar = Car.createCar(context, mServiceConnection);
+ };
+
+ /**
+ * Starts monitoring any changes in {@link CarUxRestrictions}.
+ *
+ * <p>This method can be called from {@code Activity}'s {@link Activity#onStart()}, or at the
+ * time of construction.
+ *
+ * <p>This method must be accompanied with a matching {@link #stop()} to avoid leak.
+ */
+ public void start() {
+ try {
+ if (mCar != null && !mCar.isConnected()) {
+ mCar.connect();
+ }
+ } catch (IllegalStateException e) {
+ // Do nothing.
+ Log.w(TAG, "start(); cannot connect to Car");
+ }
+ }
+
+ /**
+ * Stops monitoring any changes in {@link CarUxRestrictions}.
+ *
+ * <p>This method should be called from {@code Activity}'s {@link Activity#onStop()}, or at the
+ * time of this adapter being discarded.
+ */
+ public void stop() {
+ try {
+ if (mCar != null && mCar.isConnected()) {
+ mCar.disconnect();
+ }
+ } catch (IllegalStateException e) {
+ // Do nothing.
+ Log.w(TAG, "stop(); cannot disconnect from Car");
+ }
+ }
+
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ try {
+ mCarUxRestrictionsManager = (CarUxRestrictionsManager)
+ mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionsManager.registerListener(mListener);
+
+ mListener.onUxRestrictionsChanged(
+ mCarUxRestrictionsManager.getCurrentCarUxRestrictions());
+ } catch (CarNotConnectedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ try {
+ mCarUxRestrictionsManager.unregisterListener();
+ mCarUxRestrictionsManager = null;
+ } catch (CarNotConnectedException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+}
diff --git a/src/com/android/car/settings/quicksettings/QuickSettingActivity.java b/src/com/android/car/settings/quicksettings/QuickSettingActivity.java
index 9edc015..be2ac37 100644
--- a/src/com/android/car/settings/quicksettings/QuickSettingActivity.java
+++ b/src/com/android/car/settings/quicksettings/QuickSettingActivity.java
@@ -15,35 +15,77 @@
*/
package com.android.car.settings.quicksettings;
+import android.app.Dialog;
+import android.car.drivingstate.CarUxRestrictions;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
-import android.widget.Button;
+import android.view.View.OnClickListener;
import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.car.app.CarAlertDialog;
import androidx.car.widget.PagedListView;
import com.android.car.settings.R;
import com.android.car.settings.common.CarSettingActivity;
+import com.android.car.settings.common.CarUxRestrictionsHelper;
+import com.android.car.settings.users.UserIconProvider;
+import com.android.settingslib.users.UserManagerHelper;
/**
* Shows a page to access frequently used settings.
*/
public class QuickSettingActivity extends AppCompatActivity {
private static final String TAG = "QS";
+ private static final String DIALOG_TAG = "block_dialog_tag";
+ private static final float RESTRICTED_ALPHA = 0.5f;
+ private static final float UNRESTRICTED_ALPHA = 1f;
+
+ private CarUxRestrictionsHelper mUxRestrictionsHelper;
+ private UserManagerHelper mUserManagerHelper;
private QuickSettingGridAdapter mGridAdapter;
private PagedListView mListView;
+ private View mFullSettingBtn;
+ private View mUserSwitcherBtn;
+ private final OnClickListener mLaunchSettingListener = v -> {
+ Intent intent = new Intent(this, CarSettingActivity.class);
+ startActivity(intent);
+ };
+
+ private final OnClickListener mBlockingListener = v -> {
+ AlertDialogFragment alertDialog = new AlertDialogFragment();
+ alertDialog.show(getSupportFragmentManager(), DIALOG_TAG);
+ };
+
+ /**
+ * Shows a dialog to notify user that the actions is not available while driving.
+ */
+ public static class AlertDialogFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getContext();
+ return new CarAlertDialog.Builder(context)
+ .setBody(context.getString(R.string.restricted_while_driving))
+ .setPositiveButton(context.getString(R.string.okay),
+ /* listener= */ null)
+ .create();
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mUserManagerHelper = new UserManagerHelper(this);
setContentView(R.layout.quick_settings);
-
mListView = (PagedListView) findViewById(R.id.list);
mGridAdapter = new QuickSettingGridAdapter(this);
mListView.getRecyclerView().setLayoutManager(mGridAdapter.getGridLayoutManager());
@@ -53,28 +95,33 @@
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setCustomView(R.layout.action_bar_with_button);
+ actionBar.setCustomView(R.layout.action_bar_quick_settings);
actionBar.setDisplayShowCustomEnabled(true);
- Button adavancedSettingBtn = (Button) findViewById(R.id.action_button1);
- Button userSwitcherBtn = (Button) findViewById(R.id.action_button2);
- adavancedSettingBtn.setText(R.string.advanced_settings_label);
- adavancedSettingBtn.setVisibility(View.VISIBLE);
- adavancedSettingBtn.setOnClickListener(v -> {
- Intent intent = new Intent(this, CarSettingActivity.class);
- startActivity(intent);
- });
-
- userSwitcherBtn.setText(R.string.user_and_account_settings_title);
- userSwitcherBtn.setVisibility(View.VISIBLE);
- userSwitcherBtn.setOnClickListener(v -> {
+ mFullSettingBtn = findViewById(R.id.full_setting_btn);
+ mUserSwitcherBtn = findViewById(R.id.user_switcher_btn);
+ mUserSwitcherBtn.setOnClickListener(v -> {
Intent intent = new Intent(this, CarSettingActivity.class);
intent.setAction(CarSettingActivity.ACTION_LIST_USER);
startActivity(intent);
});
- View exitBtn = findViewById(R.id.back_button);
- ((ImageView) exitBtn).setImageResource(R.drawable.ic_close);
+
+ setupAccountButton();
+ View exitBtn = findViewById(R.id.exit_button);
exitBtn.setOnClickListener(v -> finish());
+ mUxRestrictionsHelper =
+ new CarUxRestrictionsHelper(
+ this, QuickSettingActivity.this::onUxRestrictionChagned);
+ }
+
+ private void setupAccountButton() {
+ ImageView userIcon = (ImageView) findViewById(R.id.user_icon);
+ UserInfo currentUserInfo = mUserManagerHelper.getForegroundUserInfo();
+ userIcon.setImageDrawable(
+ UserIconProvider.getUserIcon(currentUserInfo, mUserManagerHelper, this));
+
+ TextView userSwitcherText = (TextView) findViewById(R.id.user_switcher_text);
+ userSwitcherText.setText(currentUserInfo.name);
}
@Override
@@ -87,11 +134,31 @@
.addTile(new DayNightTile(this, mGridAdapter))
.addSeekbarTile(new BrightnessTile(this));
mListView.setAdapter(mGridAdapter);
+ mUxRestrictionsHelper.start();
}
@Override
public void onStop() {
super.onStop();
mGridAdapter.stop();
+ mUxRestrictionsHelper.stop();
+ }
+
+ private void onUxRestrictionChagned(CarUxRestrictions carUxRestrictions) {
+ // TODO: update tiles
+ applyRestriction(
+ (carUxRestrictions.getActiveRestrictions()
+ & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
+ == CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP);
+ }
+
+ private void applyRestriction(boolean restricted) {
+ if (restricted) {
+ mFullSettingBtn.setAlpha(RESTRICTED_ALPHA);
+ mFullSettingBtn.setOnClickListener(mBlockingListener);
+ } else {
+ mFullSettingBtn.setAlpha(UNRESTRICTED_ALPHA);
+ mFullSettingBtn.setOnClickListener(mLaunchSettingListener);
+ }
}
}