diff options
author | Pavel Maltsev <pavelm@google.com> | 2017-07-16 19:48:57 -0700 |
---|---|---|
committer | Pavel Maltsev <pavelm@google.com> | 2017-07-19 19:23:12 -0700 |
commit | 905968cf95d4c8608d6d9351b5dd10fe994a1220 (patch) | |
tree | a05c210fae4e5139f12ce852d7066e17fd2abc32 /tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster | |
parent | 909546fc1aea53bb86fb1a91d11a11bd2cd4befd (diff) | |
download | Car-905968cf95d4c8608d6d9351b5dd10fe994a1220.tar.gz |
Allow activities in instrument cluster
- Added new CarInstrumentClusterManager to start activity in the cluster
and subscribe to cluster specific events
- Cluster vendor implementation (InstrumentClusterRenderingService) was
extended, now it can notify Car Service with ActivityOptions that holds
info how to launch activity in the cluster for specific category, also it
can send additional info such as unobscured bounds
(ClusterActivityState)
- added DirectRenderingClusterSample which is an example of vendor
implementation that utilizes new features
- added FakeClusterNavigationActivity in Kitchensink which is a dummy nav
activity that can be run in the cluster, it has appropriate permissions
and activity category in manifest
Test: kitchensink
Bug: b/37500371
Change-Id: Ic4b3709a3b7e1310dbd1c499990eea64479b3333
Diffstat (limited to 'tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster')
2 files changed, 189 insertions, 27 deletions
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java new file mode 100644 index 0000000000..964d8128ee --- /dev/null +++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.car.kitchensink.cluster; + +import android.app.Activity; +import android.car.cluster.CarInstrumentClusterManager; +import android.car.cluster.ClusterActivityState; +import android.graphics.Rect; +import android.os.Bundle; +import android.support.car.Car; +import android.support.car.CarConnectionCallback; +import android.support.car.CarNotConnectedException; +import android.util.Log; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.google.android.car.kitchensink.R; + +/** + * Fake navigation activity for instrument cluster. + */ +public class FakeClusterNavigationActivity + extends Activity + implements CarInstrumentClusterManager.Callback { + + private final static String TAG = FakeClusterNavigationActivity.class.getSimpleName(); + + private Car mCarApi; + private CarInstrumentClusterManager mClusterManager; + private ImageView mUnobscuredArea; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.i(TAG, "onCreate"); + setContentView(R.layout.fake_cluster_navigation_activity); + mUnobscuredArea = findViewById(R.id.unobscuredArea); + + mCarApi = Car.createCar(this /* context */, new CarConnectionCallback() { + + @Override + public void onConnected(Car car) { + onCarConnected(car); + } + + @Override + public void onDisconnected(Car car) { + onCarDisconnected(car); + } + }); + Log.i(TAG, "Connecting to car api..."); + mCarApi.connect(); + } + + + @Override + public void onClusterActivityStateChanged(String category, Bundle clusterActivityState) { + ClusterActivityState state = ClusterActivityState.fromBundle(clusterActivityState); + Log.i(TAG, "onClusterActivityStateChanged, category: " + category + ", state: " + state); + + Rect unobscured = state.getUnobscuredBounds(); + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( + unobscured.width(), unobscured.height()); + lp.setMargins(unobscured.left, unobscured.top, 0, 0); + mUnobscuredArea.setLayoutParams(lp); + } + + private void onCarConnected(Car car) { + Log.i(TAG, "onCarConnected, car: " + car); + try { + mClusterManager = (CarInstrumentClusterManager) car.getCarManager( + android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE); + } catch (CarNotConnectedException e) { + throw new IllegalStateException(e); + } + + try { + Log.i(TAG, "registering callback..."); + mClusterManager.registerCallback(CarInstrumentClusterManager.CATEGORY_NAVIGATION, this); + Log.i(TAG, "callback registered"); + } catch (android.car.CarNotConnectedException e) { + throw new IllegalStateException(e); + } + } + + private void onCarDisconnected(Car car) { + + } +}
\ No newline at end of file diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java index 28e0a5d3e7..cfae45f775 100644 --- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java +++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java @@ -16,18 +16,22 @@ package com.google.android.car.kitchensink.cluster; import android.app.AlertDialog; +import android.car.cluster.CarInstrumentClusterManager; +import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.car.Car; import android.support.car.CarAppFocusManager; -import android.support.car.CarNotConnectedException; import android.support.car.CarConnectionCallback; +import android.support.car.CarNotConnectedException; import android.support.car.navigation.CarNavigationStatusManager; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import com.google.android.car.kitchensink.R; @@ -37,30 +41,30 @@ import com.google.android.car.kitchensink.R; public class InstrumentClusterFragment extends Fragment { private static final String TAG = InstrumentClusterFragment.class.getSimpleName(); + private static final int DISPLAY_IN_CLUSTER_PERMISSION_REQUEST = 1; + private CarNavigationStatusManager mCarNavigationStatusManager; private CarAppFocusManager mCarAppFocusManager; private Car mCarApi; - private final CarConnectionCallback mCarConnectionCallback = - new CarConnectionCallback() { - @Override - public void onConnected(Car car) { - Log.d(TAG, "Connected to Car Service"); - try { - mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi.getCarManager( - android.car.Car.CAR_NAVIGATION_SERVICE); - mCarAppFocusManager = - (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } + private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() { + @Override + public void onConnected(Car car) { + Log.d(TAG, "Connected to Car Service"); + try { + mCarNavigationStatusManager = + mCarApi.getCarManager(CarNavigationStatusManager.class); + mCarAppFocusManager = mCarApi.getCarManager(CarAppFocusManager.class); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Car is not connected!", e); } + } - @Override - public void onDisconnected(Car car) { - Log.d(TAG, "Disconnect from Car Service"); - } - }; + @Override + public void onDisconnected(Car car) { + Log.d(TAG, "Disconnect from Car Service"); + } + }; private void initCarApi() { if (mCarApi != null && mCarApi.isConnected()) { @@ -80,6 +84,7 @@ public class InstrumentClusterFragment extends Fragment { view.findViewById(R.id.cluster_start_button).setOnClickListener(v -> initCluster()); view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(v -> turnLeft()); + view.findViewById(R.id.cluster_start_activity).setOnClickListener(v -> startNavActivity()); return view; } @@ -91,6 +96,31 @@ public class InstrumentClusterFragment extends Fragment { super.onCreate(savedInstanceState); } + private void startNavActivity() { + CarInstrumentClusterManager clusterManager; + try { + clusterManager = (CarInstrumentClusterManager) mCarApi.getCarManager( + android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Failed to get CarInstrumentClusterManager", e); + Toast.makeText(getContext(), "Failed to get CarInstrumentClusterManager", + Toast.LENGTH_LONG).show(); + return; + } + + // Implicit intent + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(CarInstrumentClusterManager.CATEGORY_NAVIGATION); + try { + clusterManager.startActivity(intent); + } catch (android.car.CarNotConnectedException e) { + Log.e(TAG, "Failed to startActivity in cluster", e); + Toast.makeText(getContext(), "Failed to start activity in cluster", + Toast.LENGTH_LONG).show(); + return; + } + } + private void turnLeft() { try { mCarNavigationStatusManager @@ -100,18 +130,20 @@ public class InstrumentClusterFragment extends Fragment { CarNavigationStatusManager.DISTANCE_METERS); } catch (CarNotConnectedException e) { e.printStackTrace(); - initCarApi(); // This might happen due to inst cluster renderer crash. } } private void initCluster() { try { - mCarAppFocusManager.addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() { - @Override - public void onAppFocusChanged(CarAppFocusManager manager, int appType, boolean active) { - Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: " + active); - } - }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); + mCarAppFocusManager + .addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() { + @Override + public void onAppFocusChanged(CarAppFocusManager manager, int appType, + boolean active) { + Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: " + + active); + } + }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION); } catch (CarNotConnectedException e) { Log.e(TAG, "Failed to register focus listener", e); } @@ -126,6 +158,7 @@ public class InstrumentClusterFragment extends Fragment { .setMessage(R.string.cluster_nav_app_context_loss) .show(); } + @Override public void onAppFocusOwnershipGranted(CarAppFocusManager manager, int focus) { Log.w(TAG, "onAppFocusOwnershipGranted, focus: " + focus); @@ -155,7 +188,33 @@ public class InstrumentClusterFragment extends Fragment { .sendNavigationStatus(CarNavigationStatusManager.STATUS_ACTIVE); } catch (CarNotConnectedException e) { Log.e(TAG, "Failed to set navigation status, reconnecting to the car", e); - initCarApi(); // This might happen due to inst cluster renderer crash. + } + } + + @Override + public void onResume() { + super.onResume(); + Log.i(TAG, "onResume!"); + if (getActivity().checkSelfPermission(android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER) + != PackageManager.PERMISSION_GRANTED) { + Log.i(TAG, "Requesting: " + android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER); + + requestPermissions(new String[] {android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER}, + DISPLAY_IN_CLUSTER_PERMISSION_REQUEST); + } else { + Log.i(TAG, "All required permissions granted"); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, + int[] grantResults) { + if (DISPLAY_IN_CLUSTER_PERMISSION_REQUEST == requestCode) { + for (int i = 0; i < permissions.length; i++) { + boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED; + Log.i(TAG, "onRequestPermissionsResult, requestCode: " + requestCode + + ", permission: " + permissions[i] + ", granted: " + granted); + } } } } |