Merge "Treble-ize tmpfs access"
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
index 1dc40f6..4e8e1b3 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/notification_fragment.xml
@@ -13,73 +13,138 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Ongoing notifications can only be dismissed by the CANCEL ALL button."/>
+
<Button
android:id="@+id/cancel_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_margin="10dp"
android:text="Cancel All"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/importance_high_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Importance: HIGH (Shows heads-up)"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/importance_high_button_2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Importance: HIGH (Shows heads-up) 2"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/importance_default_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Importance: DEFAULT"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/importance_low_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Importance: LOW"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/importance_min_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Importance: MIN"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/ongoing_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Ongoing"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/category_message_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Category: CATEGORY_MESSAGE"
- android:textSize="35sp"/>
+ android:textSize="30sp"/>
+
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/category_car_emerg_button"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Category: CATEGORY_EMERG"
- android:textSize="35sp"/>
- <Button
- android:id="@+id/category_car_warning_button"
- android:layout_width="wrap_content"
+ android:background="#334666"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/category_message_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Message"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/navigation_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Navigation"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/ongoing_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Persistent (No heads-up)"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/progress_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Progress (No heads-up)"
+ android:textSize="30sp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Category: CATEGORY_WARN"
- android:textSize="35sp"/>
+ android:background="#5a6633"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/importance_high_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Importance: HIGH"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/importance_default_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Importance: DEFAULT (No heads-up)"
+ android:textSize="30sp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#5a6633"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/importance_low_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Importance: LOW (No heads-up)"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/importance_min_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Importance: MIN (No heads-up)"
+ android:textSize="30sp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#33664d"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/category_car_emergency_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Car Emergency"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/category_car_warning_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Car Warning"
+ android:textSize="30sp"/>
+
+ <Button
+ android:id="@+id/category_car_info_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="Car Information (No heads-up)"
+ android:textSize="30sp"/>
</LinearLayout>
</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/users.xml b/tests/EmbeddedKitchenSinkApp/res/layout/users.xml
new file mode 100644
index 0000000..5a862bf
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/users.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <ListView
+ android:id="@+id/user_restrictions_list"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:scrollbars="vertical"/>
+
+ <Button
+ android:id="@+id/apply_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/users_button_padding"
+ android:textSize="@dimen/users_button_text_size"
+ android:text="@string/users_apply_button" />
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
index eb6b739..437d69d 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -63,4 +63,10 @@
<dimen name="car_keyline_2">108dp</dimen>
<dimen name="car_keyline_3">152dp</dimen>
<dimen name="car_keyline_4">182dp</dimen>
+
+ <!-- Users -->
+ <dimen name="users_button_padding">10dp</dimen>
+ <dimen name="users_button_text_size">24sp</dimen>
+ <dimen name="users_checkbox_padding">5dp</dimen>
+ <dimen name="users_checkbox_text_size">32sp</dimen>
</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 5894892..f1a645e 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -301,4 +301,7 @@
<string name="weblink_nytimes" translatable="false">www.nytimes.com</string>
<string name="weblink_support_name" translatable="false">support.google.com</string>
<string name="weblink_support" translatable="false">https://support.google.com/chrome/answer/95414?hl=en&ref_topic=7438008</string>
+
+ <!-- Users -->
+ <string name="users_apply_button" translatable="false">Apply</string>
</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 4d888d3..3c220ca 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -61,11 +61,12 @@
import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
+import com.google.android.car.kitchensink.users.UsersFragment;
import com.google.android.car.kitchensink.vhal.VehicleHalFragment;
import com.google.android.car.kitchensink.volume.VolumeTestFragment;
import com.google.android.car.kitchensink.weblinks.WebLinksTestFragment;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
@@ -145,47 +146,40 @@
}
}
- private final List<MenuEntry> mMenuEntries = new ArrayList<MenuEntry>() {
- {
- add("alert window", AlertDialogTestFragment.class);
- add("assistant", CarAssistantFragment.class);
- add("audio", AudioTestFragment.class);
- add("bluetooth headset",BluetoothHeadsetFragment.class);
- add("bluetooth messaging test", MapMceTestFragment.class);
- add("carboard", KeyboardTestFragment.class);
- add("cubes test", CubesTestFragment.class);
- add("diagnostic", DiagnosticTestFragment.class);
- add("display info", DisplayInfoFragment.class);
- add("hvac", HvacTestFragment.class);
- add("inst cluster", InstrumentClusterFragment.class);
- add("input test", InputTestFragment.class);
- add("notification", NotificationFragment.class);
- add("orientation test", OrientationTestFragment.class);
- add("power test", PowerTestFragment.class);
- add("property test", PropertyTestFragment.class);
- add("sensors", SensorsTestFragment.class);
- add("storage lifetime", StorageLifetimeFragment.class);
- add("touch test", TouchTestFragment.class);
- add("volume test", VolumeTestFragment.class);
- add("vehicle hal", VehicleHalFragment.class);
- add("car service settings", () -> {
+ private final List<MenuEntry> mMenuEntries = Arrays.asList(
+ new FragmentMenuEntry("activity view", ActivityViewTestFragment.class),
+ new FragmentMenuEntry("alert window", AlertDialogTestFragment.class),
+ new FragmentMenuEntry("assistant", CarAssistantFragment.class),
+ new FragmentMenuEntry("audio", AudioTestFragment.class),
+ new FragmentMenuEntry("bluetooth headset", BluetoothHeadsetFragment.class),
+ new FragmentMenuEntry("bluetooth messaging test", MapMceTestFragment.class),
+ new OnClickMenuEntry("car service settings", () -> {
Intent intent = new Intent(KitchenSinkActivity.this,
- CarServiceSettingsActivity.class);
+ CarServiceSettingsActivity.class);
startActivity(intent);
- });
- add("activity view", ActivityViewTestFragment.class);
- add("connectivity", ConnectivityFragment.class);
- add("web links", WebLinksTestFragment.class);
- add("quit", KitchenSinkActivity.this::finish);
- }
+ }),
+ new FragmentMenuEntry("carboard", KeyboardTestFragment.class),
+ new FragmentMenuEntry("connectivity", ConnectivityFragment.class),
+ new FragmentMenuEntry("cubes test", CubesTestFragment.class),
+ new FragmentMenuEntry("diagnostic", DiagnosticTestFragment.class),
+ new FragmentMenuEntry("display info", DisplayInfoFragment.class),
+ new FragmentMenuEntry("hvac", HvacTestFragment.class),
+ new FragmentMenuEntry("inst cluster", InstrumentClusterFragment.class),
+ new FragmentMenuEntry("input test", InputTestFragment.class),
+ new FragmentMenuEntry("notification", NotificationFragment.class),
+ new FragmentMenuEntry("orientation test", OrientationTestFragment.class),
+ new FragmentMenuEntry("power test", PowerTestFragment.class),
+ new FragmentMenuEntry("property test", PropertyTestFragment.class),
+ new FragmentMenuEntry("sensors", SensorsTestFragment.class),
+ new FragmentMenuEntry("storage lifetime", StorageLifetimeFragment.class),
+ new FragmentMenuEntry("touch test", TouchTestFragment.class),
+ new FragmentMenuEntry("users", UsersFragment.class),
+ new FragmentMenuEntry("volume test", VolumeTestFragment.class),
+ new FragmentMenuEntry("vehicle hal", VehicleHalFragment.class),
+ new FragmentMenuEntry("web links", WebLinksTestFragment.class),
+ new OnClickMenuEntry("quit", KitchenSinkActivity.this::finish)
+ );
- <T extends Fragment> void add(String text, Class<T> clazz) {
- add(new FragmentMenuEntry(text, clazz));
- }
- void add(String text, ClickHandler onClick) {
- add(new OnClickMenuEntry(text, onClick));
- }
- };
private Car mCarApi;
private CarHvacManager mHvacManager;
private CarPowerManager mPowerManager;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
index 4de7e5a..e458df2 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/notification/NotificationFragment.java
@@ -1,220 +1,400 @@
package com.google.android.car.kitchensink.notification;
-import static android.security.KeyStore.getApplicationContext;
-
import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationCompat.Action;
+import androidx.core.app.NotificationCompat.MessagingStyle;
+import androidx.core.app.Person;
+import androidx.core.app.RemoteInput;
import androidx.fragment.app.Fragment;
import com.google.android.car.kitchensink.KitchenSinkActivity;
import com.google.android.car.kitchensink.R;
+import java.util.HashMap;
+
/**
* Test fragment that can send all sorts of notifications.
*/
public class NotificationFragment extends Fragment {
- private static final String CHANNEL_ID_1 = "kitchensink.channel1";
- private static final String CHANNEL_ID_2 = "kitchensink.channel2";
- private static final String CHANNEL_ID_3 = "kitchensink.channel3";
- private static final String CHANNEL_ID_4 = "kitchensink.channel4";
- private static final String CHANNEL_ID_5 = "kitchensink.channel5";
- private static final String CHANNEL_ID_6 = "kitchensink.channel6";
+ private static final String IMPORTANCE_HIGH_ID = "importance_high";
+ private static final String IMPORTANCE_HIGH_NO_SOUND_ID = "importance_high_no_sound";
+ private static final String IMPORTANCE_DEFAULT_ID = "importance_default";
+ private static final String IMPORTANCE_LOW_ID = "importance_low";
+ private static final String IMPORTANCE_MIN_ID = "importance_min";
+ private static final String IMPORTANCE_NONE_ID = "importance_none";
+ private int mCurrentNotificationId = 0;
+ private NotificationManager mManager;
+ private Context mContext;
+ private Handler mHandler = new Handler();
+ private HashMap<Integer, Runnable> mUpdateRunnables = new HashMap<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mManager =
+ (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
+ mContext = getActivity();
+
+ mManager.createNotificationChannel(new NotificationChannel(
+ IMPORTANCE_HIGH_ID, "Importance High", NotificationManager.IMPORTANCE_HIGH));
+
+ NotificationChannel noSoundChannel = new NotificationChannel(
+ IMPORTANCE_HIGH_NO_SOUND_ID, "No sound", NotificationManager.IMPORTANCE_HIGH);
+ noSoundChannel.setSound(null, null);
+ mManager.createNotificationChannel(noSoundChannel);
+
+ mManager.createNotificationChannel(new NotificationChannel(
+ IMPORTANCE_DEFAULT_ID,
+ "Importance Default",
+ NotificationManager.IMPORTANCE_DEFAULT));
+
+ mManager.createNotificationChannel(new NotificationChannel(
+ IMPORTANCE_LOW_ID, "Importance Low", NotificationManager.IMPORTANCE_LOW));
+
+ mManager.createNotificationChannel(new NotificationChannel(
+ IMPORTANCE_MIN_ID, "Importance Min", NotificationManager.IMPORTANCE_MIN));
+
+ mManager.createNotificationChannel(new NotificationChannel(
+ IMPORTANCE_NONE_ID, "Importance None", NotificationManager.IMPORTANCE_NONE));
+ }
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.notification_fragment, container, false);
- Button cancelAllButton = view.findViewById(R.id.cancel_all_button);
- Button importanceHighButton = view.findViewById(R.id.importance_high_button);
- Button importanceHighButton2 = view.findViewById(R.id.importance_high_button_2);
- Button importanceLowButton = view.findViewById(R.id.importance_low_button);
- Button importanceMinButton = view.findViewById(R.id.importance_min_button);
- Button importanceDefaultButton = view.findViewById(R.id.importance_default_button);
- Button ongoingButton = view.findViewById(R.id.ongoing_button);
- Button messageButton = view.findViewById(R.id.category_message_button);
- Button emerg = view.findViewById(R.id.category_car_emerg_button);
- Button warn = view.findViewById(R.id.category_car_warning_button);
- NotificationManager manager =
- (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
+ initCancelAllButton(view);
- // cancel all button
- cancelAllButton.setOnClickListener(v -> manager.cancelAll());
+ initCarCategoriesButton(view);
- // importance high notifications
- NotificationChannel highImportanceChannel =
- new NotificationChannel(
- CHANNEL_ID_1, "Importance High", NotificationManager.IMPORTANCE_HIGH);
- manager.createNotificationChannel(highImportanceChannel);
+ initImportanceHighBotton(view);
+ initImportanceDefaultButton(view);
+ initImportanceLowButton(view);
+ initImportanceMinButton(view);
- importanceHighButton.setOnClickListener(v -> {
+ initOngoingButton(view);
+ initMessagingStyleButton(view);
+ initProgressButton(view);
+ initNavigationButton(view);
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_1)
- .setContentTitle("Importance High")
- .setContentText("blah")
+ return view;
+ }
+
+ private void initCancelAllButton(View view) {
+ view.findViewById(R.id.cancel_all_button).setOnClickListener(v -> {
+ for (Runnable runnable : mUpdateRunnables.values()) {
+ mHandler.removeCallbacks(runnable);
+ }
+ mUpdateRunnables.clear();
+ mManager.cancelAll();
+ });
+ }
+
+ private void initCarCategoriesButton(View view) {
+ view.findViewById(R.id.category_car_emergency_button).setOnClickListener(v -> {
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("Car Emergency")
+ .setContentText("Shows heads-up; Shows on top of the list; Does not group")
+ .setCategory(Notification.CATEGORY_CAR_EMERGENCY)
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(1, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
- importanceHighButton2.setOnClickListener(v -> {
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_1)
- .setContentTitle("Importance High 2")
- .setContentText("blah blah blah")
+ view.findViewById(R.id.category_car_warning_button).setOnClickListener(v -> {
+
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_MIN_ID)
+ .setContentTitle("Car Warning")
+ .setContentText(
+ "Shows heads-up; Shows on top of the list but below Car Emergency; "
+ + "Does not group")
+ .setCategory(Notification.CATEGORY_CAR_WARNING)
+ .setColor(mContext.getColor(android.R.color.holo_orange_dark))
+ .setColorized(true)
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(2, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
- // importance default
- importanceDefaultButton.setOnClickListener(v -> {
- NotificationChannel channel =
- new NotificationChannel(
- CHANNEL_ID_3,
- "Importance Default",
- NotificationManager.IMPORTANCE_DEFAULT);
- manager.createNotificationChannel(channel);
-
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_3)
- .setContentTitle("Importance Default")
+ view.findViewById(R.id.category_car_info_button).setOnClickListener(v -> {
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("Car information")
+ .setContentText("Doesn't show heads-up; Importance Default; Groups")
+ .setCategory(Notification.CATEGORY_CAR_INFORMATION)
+ .setColor(mContext.getColor(android.R.color.holo_orange_light))
+ .setColorized(true)
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(4, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
- // importance low
- importanceLowButton.setOnClickListener(v -> {
- NotificationChannel channel =
- new NotificationChannel(
- CHANNEL_ID_4, "Importance Low", NotificationManager.IMPORTANCE_LOW);
- manager.createNotificationChannel(channel);
+ }
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_4)
+ private void initImportanceHighBotton(View view) {
+ Intent mIntent = new Intent(getActivity(), KitchenSinkActivity.class);
+ PendingIntent mPendingIntent = PendingIntent.getActivity(getActivity(), 0, mIntent, 0);
+
+ Notification notification1 = new Notification
+ .Builder(getActivity(), IMPORTANCE_HIGH_ID)
+ .setContentTitle("Importance High: Shows as a heads-up")
+ .setContentText(
+ "Each click generates a new notification. And some "
+ + "looooooong text. "
+ + "Loooooooooooooooooooooong. "
+ + "Loooooooooooooooooooooooooooooooooooooooooooooooooong.")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .addAction(
+ new Notification.Action.Builder(
+ null, "Long Action (no-op)", mPendingIntent).build())
+ .addAction(
+ new Notification.Action.Builder(
+ null, "Action (no-op)", mPendingIntent).build())
+ .addAction(
+ new Notification.Action.Builder(
+ null, "Long Action (no-op)", mPendingIntent).build())
+ .setColor(mContext.getColor(android.R.color.holo_red_light))
+ .build();
+
+ view.findViewById(R.id.importance_high_button).setOnClickListener(
+ v -> mManager.notify(mCurrentNotificationId++, notification1)
+ );
+ }
+
+ private void initImportanceDefaultButton(View view) {
+ view.findViewById(R.id.importance_default_button).setOnClickListener(v -> {
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("No heads-up; Importance Default; Groups")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .build();
+ mManager.notify(mCurrentNotificationId++, notification);
+ });
+ }
+
+ private void initImportanceLowButton(View view) {
+ view.findViewById(R.id.importance_low_button).setOnClickListener(v -> {
+
+ Notification notification = new Notification.Builder(getActivity(), IMPORTANCE_LOW_ID)
.setContentTitle("Importance Low")
- .setContentText("low low low")
+ .setContentText("No heads-up; Below Importance Default; Groups")
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(5, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
+ }
- // importance min
- importanceMinButton.setOnClickListener(v -> {
- NotificationChannel channel =
- new NotificationChannel(
- CHANNEL_ID_5, "Importance Min", NotificationManager.IMPORTANCE_MIN);
- manager.createNotificationChannel(channel);
+ private void initImportanceMinButton(View view) {
+ view.findViewById(R.id.importance_min_button).setOnClickListener(v -> {
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_5)
+ Notification notification = new Notification.Builder(getActivity(), IMPORTANCE_MIN_ID)
.setContentTitle("Importance Min")
- .setContentText("min min min")
+ .setContentText("No heads-up; Below Importance Low; Groups")
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(6, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
+ }
- // ongoing
- ongoingButton.setOnClickListener(v -> {
- NotificationChannel channel =
- new NotificationChannel(
- CHANNEL_ID_6, "Ongoing", NotificationManager.IMPORTANCE_DEFAULT);
- manager.createNotificationChannel(channel);
+ private void initOngoingButton(View view) {
+ view.findViewById(R.id.ongoing_button).setOnClickListener(v -> {
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_6)
- .setContentTitle("Playing music or something")
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("Persistent/Ongoing Notification")
+ .setContentText("Cannot be dismissed; No heads-up; Importance default; Groups")
.setSmallIcon(R.drawable.car_ic_mode)
.setOngoing(true)
.build();
- manager.notify(7, notification);
+ mManager.notify(mCurrentNotificationId++, notification);
});
+ }
- // category message
- messageButton.setOnClickListener(v -> {
- NotificationChannel channel =
- new NotificationChannel(
- CHANNEL_ID_2, "Message", NotificationManager.IMPORTANCE_HIGH);
- manager.createNotificationChannel(channel);
+ private void initMessagingStyleButton(View view) {
+ int id = mCurrentNotificationId++;
- Intent intent = new Intent(getActivity(), KitchenSinkActivity.class);
- PendingIntent readIntent = PendingIntent.getActivity(getActivity(), 0, intent, 0);
+ view.findViewById(R.id.category_message_button).setOnClickListener(v -> {
- RemoteInput remoteInput = new RemoteInput.Builder("voice reply").build();
- PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
- 12345,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent replyIntent = createServiceIntent(id, "reply");
+ PendingIntent markAsReadIntent = createServiceIntent(id, "read");
Person personJohn = new Person.Builder()
.setName("John Doe")
- .setIcon(Icon.createWithResource(v.getContext(), R.drawable.avatar1))
.build();
Person personJane = new Person.Builder()
.setName("Jane Roe")
- .setIcon(Icon.createWithResource(v.getContext(), R.drawable.avatar2))
.build();
- Notification.MessagingStyle messagingStyle =
- new Notification.MessagingStyle(personJohn)
- .setConversationTitle("Whassup")
- .addHistoricMessage(
- new Notification.MessagingStyle.Message(
- "historic message",
+ MessagingStyle messagingStyle =
+ new MessagingStyle(personJohn)
+ .setConversationTitle("Heads-up: New Message")
+ .addMessage(
+ new MessagingStyle.Message(
+ "The meaning of life, or the answer to the question"
+ + "What is the meaning of life?, pertains to "
+ + "the significance of living or existence in"
+ + " general. Many other related questions "
+ + "include: Why are we here?, What is "
+ + "life all about?, or What is the "
+ + "purpose of existence?",
System.currentTimeMillis() - 3600,
personJohn))
- .addMessage(new Notification.MessagingStyle.Message(
- "message", System.currentTimeMillis(), personJane));
+ .addMessage(
+ new MessagingStyle.Message(
+ "Importance High; Groups", System.currentTimeMillis(),
+ personJane));
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_2)
+ NotificationCompat.Builder notification = new NotificationCompat
+ .Builder(getActivity(), IMPORTANCE_HIGH_ID)
.setContentTitle("Message from someone")
.setContentText("hi")
+ .setShowWhen(true)
.setCategory(Notification.CATEGORY_MESSAGE)
.setSmallIcon(R.drawable.car_ic_mode)
.setStyle(messagingStyle)
.setAutoCancel(true)
+ .setColor(mContext.getColor(android.R.color.holo_green_light))
.addAction(
- new Notification.Action.Builder(null, "read", readIntent).build())
+ new Action.Builder(R.drawable.ic_check_box, "read", markAsReadIntent)
+ .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ)
+ .setShowsUserInterface(false)
+ .build())
.addAction(
- new Notification.Action.Builder(null, "reply", replyIntent)
- .addRemoteInput(remoteInput).build())
- .extend(new Notification.CarExtender().setColor(R.color.car_red_500))
- .build();
- manager.notify(3, notification);
+ new Action.Builder(R.drawable.ic_check_box, "reply", replyIntent)
+ .setSemanticAction(Action.SEMANTIC_ACTION_REPLY)
+ .setShowsUserInterface(false)
+ .addRemoteInput(new RemoteInput.Builder("input").build())
+ .build());
+
+ mManager.notify(id, notification.build());
});
+ }
- emerg.setOnClickListener(v -> {
+ private PendingIntent createServiceIntent(int notificationId, String action) {
+ Intent intent = new Intent(mContext, KitchenSinkActivity.class).setAction(action);
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_1)
- .setContentTitle("OMG")
- .setContentText("This is of top importance")
- .setCategory(Notification.CATEGORY_CAR_EMERGENCY)
+ return PendingIntent.getForegroundService(mContext, notificationId, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private void initProgressButton(View view) {
+ view.findViewById(R.id.progress_button).setOnClickListener(v -> {
+ int id = mCurrentNotificationId++;
+
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("Progress")
+ .setContentText("Doesn't show heads-up; Importance Default; Groups")
+ .setProgress(100, 0, false)
+ .setColor(mContext.getColor(android.R.color.holo_purple))
+ .setContentInfo("0%")
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(10, notification);
+ mManager.notify(id, notification);
+
+ Runnable runnable = new Runnable() {
+ int mProgress = 0;
+
+ @Override
+ public void run() {
+ Notification updateNotification = new Notification
+ .Builder(getActivity(), IMPORTANCE_DEFAULT_ID)
+ .setContentTitle("Progress")
+ .setContentText("Doesn't show heads-up; Importance Default; Groups")
+ .setProgress(100, mProgress, false)
+ .setColor(mContext.getColor(android.R.color.holo_purple))
+ .setContentInfo(mProgress + "%")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .build();
+ mManager.notify(id, updateNotification);
+ mProgress += 5;
+ if (mProgress <= 100) {
+ mHandler.postDelayed(this, 1000);
+ }
+ }
+ };
+ mUpdateRunnables.put(id, runnable);
+ mHandler.post(runnable);
});
+ }
- warn.setOnClickListener(v -> {
+ private void initNavigationButton(View view) {
+ view.findViewById(R.id.navigation_button).setOnClickListener(v -> {
+ int id = mCurrentNotificationId++;
- Notification notification = new Notification.Builder(getActivity(), CHANNEL_ID_1)
- .setContentTitle("OMG -ish ")
- .setContentText("This is of less importance but still")
- .setCategory(Notification.CATEGORY_CAR_WARNING)
+ Notification notification = new Notification
+ .Builder(getActivity(), IMPORTANCE_HIGH_ID)
+ .setContentTitle("Navigation")
+ .setContentText("Turn right in 900 ft")
+ .setColor(mContext.getColor(android.R.color.holo_green_dark))
+ .setColorized(true)
+ .setSubText("900 ft")
.setSmallIcon(R.drawable.car_ic_mode)
.build();
- manager.notify(11, notification);
- });
+ mManager.notify(id, notification);
- return view;
+ Runnable rightTurnRunnable = new Runnable() {
+ int mDistance = 800;
+
+ @Override
+ public void run() {
+ Notification updateNotification = new Notification
+ .Builder(getActivity(), IMPORTANCE_HIGH_NO_SOUND_ID)
+ .setContentTitle("Navigation")
+ .setContentText("Turn right in " + mDistance + " ft")
+ .setColor(mContext.getColor(android.R.color.holo_green_dark))
+ .setColorized(true)
+ .setSubText(mDistance + " ft")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .build();
+ mManager.notify(id, updateNotification);
+ mDistance -= 100;
+ if (mDistance >= 0) {
+ mHandler.postDelayed(this, 1000);
+ }
+ }
+ };
+
+ Runnable exitRunnable = new Runnable() {
+ int mDistance = 9;
+
+ @Override
+ public void run() {
+ Notification updateNotification = new Notification
+ .Builder(getActivity(), IMPORTANCE_HIGH_NO_SOUND_ID)
+ .setContentTitle("Navigation")
+ .setContentText("Exit in " + mDistance + " miles")
+ .setColor(mContext.getColor(android.R.color.holo_green_dark))
+ .setColorized(true)
+ .setSubText(mDistance + " miles")
+ .setSmallIcon(R.drawable.car_ic_mode)
+ .build();
+ mManager.notify(id, updateNotification);
+ mDistance -= 1;
+ if (mDistance >= 0) {
+ mHandler.postDelayed(this, 1000);
+ }
+ }
+ };
+
+ mUpdateRunnables.put(id, rightTurnRunnable);
+ mUpdateRunnables.put(id, exitRunnable);
+ mHandler.postDelayed(rightTurnRunnable, 1000);
+ mHandler.postDelayed(exitRunnable, 10000);
+ });
}
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionAdapter.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionAdapter.java
new file mode 100644
index 0000000..00c7192
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.google.android.car.kitchensink.users;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.List;
+
+/**
+ * Adapter to display a set of user restrictions
+ */
+public class UserRestrictionAdapter extends BaseAdapter {
+
+ private final Context mContext;
+ private final List<UserRestrictionListItem> mItems;
+
+ public UserRestrictionAdapter(Context context, List<UserRestrictionListItem> items) {
+ mContext = context;
+ mItems = items;
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int index) {
+ return mItems.get(index);
+ }
+
+ @Override
+ public long getItemId(int index) {
+ return index;
+ }
+
+ @Override
+ public View getView(int index, View convertView, ViewGroup parent) {
+ return convertView == null
+ ? createCheckBox((UserRestrictionListItem) getItem(index))
+ : convertView;
+ }
+
+ private CheckBox createCheckBox(UserRestrictionListItem item) {
+ Resources resources = mContext.getResources();
+ CheckBox checkBox = new CheckBox(mContext);
+ checkBox.setTextSize(resources.getDimensionPixelSize(R.dimen.users_checkbox_text_size));
+ int padding = resources.getDimensionPixelSize(R.dimen.users_checkbox_padding);
+ checkBox.setPadding(padding, padding, padding, padding);
+ checkBox.setText(item.getKey());
+ checkBox.setOnCheckedChangeListener((v, isChecked) -> item.setIsChecked(isChecked));
+ checkBox.setChecked(item.getIsChecked());
+ return checkBox;
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionListItem.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionListItem.java
new file mode 100644
index 0000000..0454f75
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionListItem.java
@@ -0,0 +1,42 @@
+/*
+ * 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.google.android.car.kitchensink.users;
+
+/**
+ * Represents a user restriction in a list. Contains the key for the user restriction and the
+ * "checked" status of the checkbox in the list.
+ */
+public class UserRestrictionListItem {
+ private final String mKey;
+ private boolean mIsChecked;
+
+ public UserRestrictionListItem(String key, boolean isChecked) {
+ mKey = key;
+ mIsChecked = isChecked;
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+
+ public void setIsChecked(boolean value) {
+ mIsChecked = value;
+ }
+
+ public boolean getIsChecked() {
+ return mIsChecked;
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UsersFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UsersFragment.java
new file mode 100644
index 0000000..3272c30
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UsersFragment.java
@@ -0,0 +1,96 @@
+/*
+ * 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.google.android.car.kitchensink.users;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Manipulate users in various ways
+ */
+public class UsersFragment extends Fragment {
+
+ private static final List<String> CONFIGURABLE_USER_RESTRICTIONS =
+ Arrays.asList(
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_BLUETOOTH,
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_MODIFY_ACCOUNTS,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_UNINSTALL_APPS,
+ UserManager.DISALLOW_USER_SWITCH
+ );
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.users, container, false);
+ }
+
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ ListView userRestrictionsList = view.findViewById(R.id.user_restrictions_list);
+ userRestrictionsList.setAdapter(
+ new UserRestrictionAdapter(getContext(), createUserRestrictionItems()));
+
+ Button applyButton = view.findViewById(R.id.apply_button);
+ applyButton.setOnClickListener(v -> {
+ UserRestrictionAdapter adapter =
+ (UserRestrictionAdapter) userRestrictionsList.getAdapter();
+ int count = adapter.getCount();
+ UserManager userManager =
+ (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+
+ // Iterate through all of the user restrictions and set their values
+ for (int i = 0; i < count; i++) {
+ UserRestrictionListItem item = (UserRestrictionListItem) adapter.getItem(i);
+ userManager.setUserRestriction(item.getKey(), item.getIsChecked());
+ }
+
+ Toast.makeText(
+ getContext(), "User restrictions have been set!", Toast.LENGTH_SHORT)
+ .show();
+ });
+ }
+
+ private List<UserRestrictionListItem> createUserRestrictionItems() {
+ UserManager userManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ ArrayList<UserRestrictionListItem> list = new ArrayList<>();
+ for (String key : CONFIGURABLE_USER_RESTRICTIONS) {
+ list.add(new UserRestrictionListItem(key, userManager.hasUserRestriction(key)));
+ }
+ return list;
+ }
+}
diff --git a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
index fe39d51..23186aa 100644
--- a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
@@ -68,6 +68,17 @@
);
/**
+ * Additional optional set of restrictions for Non-Admin users.
+ */
+ public static final Set<String> OPTIONAL_NON_ADMIN_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_UNINSTALL_APPS
+ );
+
+ /**
* Default set of restrictions for Guest users.
*/
private static final Set<String> DEFAULT_GUEST_RESTRICTIONS = Sets.newArraySet(
@@ -733,6 +744,7 @@
// Remove restrictions imposed on non-admins.
setDefaultNonAdminRestrictions(user, /* enable= */ false);
+ setOptionalNonAdminRestrictions(user, /* enable= */ false);
}
/**
@@ -810,6 +822,18 @@
}
/**
+ * Sets the values of settings controllable restrictions to the passed in value.
+ *
+ * @param userInfo User to set restrictions on.
+ * @param enable If true, restriction is ON, If false, restriction is OFF.
+ */
+ private void setOptionalNonAdminRestrictions(UserInfo userInfo, boolean enable) {
+ for (String restriction : OPTIONAL_NON_ADMIN_RESTRICTIONS) {
+ setUserRestriction(userInfo, restriction, enable);
+ }
+ }
+
+ /**
* Sets the value of the specified restriction for the specified user.
*
* @param userInfo the user whose restriction is to be changed