diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-05-21 07:01:27 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-05-21 07:01:27 +0000 |
commit | 83996e00f004914926ffd3254aa146b22ec6694b (patch) | |
tree | 4e8ec9980492c50ddcfb642eb953b9cc07cbb9c7 | |
parent | 7005b198d256d07d4491c2e63ab457680e27261a (diff) | |
parent | e70f99d264130e560975fd1f4931c97f65b058d0 (diff) | |
download | tests-83996e00f004914926ffd3254aa146b22ec6694b.tar.gz |
Snap for 6517089 from e70f99d264130e560975fd1f4931c97f65b058d0 to mainline-release
Change-Id: I933b8ab68973e0b6ce988c4ff881aa9fdf789423
9 files changed, 214 insertions, 6 deletions
diff --git a/TestMediaApp/Android.bp b/TestMediaApp/Android.bp index 5ce6240..a2e80e2 100644 --- a/TestMediaApp/Android.bp +++ b/TestMediaApp/Android.bp @@ -30,7 +30,7 @@ android_app { // Do NOT add dependencies preventing the app from being unbundled (compiled with gradle in Studio). static_libs: [ "androidx.appcompat_appcompat", - "androidx-constraintlayout_constraintlayout", + "androidx-constraintlayout_constraintlayout", "androidx.preference_preference", "androidx.legacy_legacy-support-v4", ], diff --git a/TestMediaApp/AndroidManifest.xml b/TestMediaApp/AndroidManifest.xml index 911145e..67f973f 100644 --- a/TestMediaApp/AndroidManifest.xml +++ b/TestMediaApp/AndroidManifest.xml @@ -20,7 +20,8 @@ <uses-feature android:name="android.hardware.type.automotive" android:required="true"/> - + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <application android:allowBackup="true" @@ -45,6 +46,13 @@ </intent-filter> </service> + <service android:name=".TmaForegroundService" + android:icon="@drawable/ic_app_icon" + android:exported="false" + android:foregroundServiceType="location" + android:label="@string/app_name"> + </service> + <service android:name=".TmaBrowser2" android:icon="@mipmap/ic_launcher" @@ -58,6 +66,7 @@ <activity android:name=".prefs.TmaPrefsActivity" + android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.APPLICATION_PREFERENCES"/> @@ -70,14 +79,16 @@ <meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc"/> - <activity android:name=".phone.TmaLauncherActivity" > + <activity android:name=".phone.TmaLauncherActivity" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <receiver android:name="androidx.media.session.MediaButtonReceiver" > + <receiver android:name="androidx.media.session.MediaButtonReceiver" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MEDIA_BUTTON" /> </intent-filter> diff --git a/TestMediaApp/assets/media_items/advanced.json b/TestMediaApp/assets/media_items/advanced.json index 45a18ef..9266a1c 100644 --- a/TestMediaApp/assets/media_items/advanced.json +++ b/TestMediaApp/assets/media_items/advanced.json @@ -54,6 +54,16 @@ "DISPLAY_TITLE": "Exceptions" }, "INCLUDE":"media_items/exceptions.json" + }, + { + "FLAGS": "playable", + "METADATA": { + "MEDIA_ID": "location", + "DISPLAY_TITLE": "Location", + "DURATION": 10000, + "ART_URI": "drawable/ic_location" + }, + "CUSTOM_ACTIONS": ["REQUEST_LOCATION"] } ] }
\ No newline at end of file diff --git a/TestMediaApp/res/drawable/ic_location.xml b/TestMediaApp/res/drawable/ic_location.xml new file mode 100644 index 0000000..1017cbc --- /dev/null +++ b/TestMediaApp/res/drawable/ic_location.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/> +</vector> diff --git a/TestMediaApp/res/values/strings.xml b/TestMediaApp/res/values/strings.xml index ac621be..1ffd39c 100644 --- a/TestMediaApp/res/values/strings.xml +++ b/TestMediaApp/res/values/strings.xml @@ -31,4 +31,5 @@ <string name="heart_less_less" translatable="false">Heart--</string> + <string name="location" translatable="false">Location</string> </resources> diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaForegroundService.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaForegroundService.java new file mode 100644 index 0000000..6241455 --- /dev/null +++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaForegroundService.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2020 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.media.testmediaapp; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.IBinder; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; + +import com.android.car.media.testmediaapp.prefs.TmaPrefsActivity; + +/** + * Service used to test and demonstrate the access to "foreground" permissions. In particular, this + * implementation deals with location access from a headless service. This service is initiated + * using {@link Service#startService(Intent)} from the browse service as a respond to a custom + * playback command. Subsequent start commands make the service toggle between running and stopping. + * + * In real applications, this service would be handling background playback, maybe using location + * and other sensors to automatically select songs. + */ +public class TmaForegroundService extends Service { + public static final String CHANNEL_ID = "ForegroundServiceChannel"; + private LocationManager mLocationManager; + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (doWork()) { + createNotificationChannel(); + Intent notificationIntent = new Intent(this, TmaPrefsActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, + 0, notificationIntent, 0); + Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("Foreground Service") + .setSmallIcon(R.drawable.ic_app_icon) + .setContentIntent(pendingIntent) + .build(); + startForeground(1, notification); + } else { + getMainExecutor().execute(this::stopSelf); + } + + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + if (mLocationManager != null) { + mLocationManager.removeUpdates(mLocationListener); + toast("Location is off"); + } + super.onDestroy(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void createNotificationChannel() { + NotificationChannel serviceChannel = new NotificationChannel( + CHANNEL_ID, + "Foreground Service Channel", + NotificationManager.IMPORTANCE_DEFAULT + ); + NotificationManager manager = getSystemService(NotificationManager.class); + manager.createNotificationChannel(serviceChannel); + } + + private boolean doWork() { + if (mLocationManager != null) { + return false; + } + mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); + try { + mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, + 0, mLocationListener); + toast("Location is on"); + } catch (Throwable e) { + toast("Unable to get location: " + e.getMessage()); + } + return true; + } + + /** + * We use toasts here as it is the only way for a headless service to show something on the + * screen. Real application shouldn't be using toasts from service. + */ + private void toast(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + private final LocationListener mLocationListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + toast("Location provider: " + location.getLatitude() + ":" + location.getLongitude()); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + toast("Location provider: " + provider + " status changed to: " + status); + } + + @Override + public void onProviderEnabled(String provider) { + toast("Location provider enabled: " + provider); + } + + @Override + public void onProviderDisabled(String provider) { + toast("Location provider disabled: " + provider); + } + }; +} diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java index 129e267..e2cb533 100644 --- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java +++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java @@ -55,7 +55,9 @@ public class TmaMediaItem { HEART_PLUS_PLUS(CUSTOM_ACTION_PREFIX + "heart_plus_plus", R.string.heart_plus_plus, R.drawable.ic_heart_plus_plus), HEART_LESS_LESS(CUSTOM_ACTION_PREFIX + "heart_less_less", R.string.heart_less_less, - R.drawable.ic_heart_less_less); + R.drawable.ic_heart_less_less), + REQUEST_LOCATION(CUSTOM_ACTION_PREFIX + "location", R.string.location, + R.drawable.ic_location); final String mId; final int mNameId; diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java index d8fab6c..65cc787 100644 --- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java +++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java @@ -30,6 +30,7 @@ import static android.support.v4.media.session.PlaybackStateCompat.ERROR_CODE_AP import static android.support.v4.media.session.PlaybackStateCompat.STATE_ERROR; import androidx.annotation.Nullable; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -77,7 +78,6 @@ public class TmaPlayer extends MediaSessionCompat.Callback { private TmaMediaItem mActiveItem; private int mNextEventIndex = -1; - TmaPlayer(Context context, TmaLibrary library, AudioManager audioManager, Handler handler, MediaSessionCompat session) { mContext = context; @@ -248,6 +248,8 @@ public class TmaPlayer extends MediaSessionCompat.Callback { } else if (TmaCustomAction.HEART_LESS_LESS.mId.equals(action)) { mActiveItem.mHearts--; toast("" + mActiveItem.mHearts); + } else if (TmaCustomAction.REQUEST_LOCATION.mId.equals(action)) { + mContext.startService(new Intent(mContext, TmaForegroundService.class)); } } } diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java index 066cc9c..d3e681e 100644 --- a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java +++ b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java @@ -16,8 +16,12 @@ package com.android.car.media.testmediaapp.prefs; +import android.Manifest; +import android.app.Activity; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.widget.Toast; import androidx.preference.DropDownPreference; import androidx.preference.Preference; @@ -30,6 +34,8 @@ import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaLoginEventOrder; import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaReplyDelay; import com.android.car.media.testmediaapp.prefs.TmaPrefs.PrefEntry; +import java.util.function.Consumer; + public class TmaPrefsFragment extends PreferenceFragmentCompat { @Override @@ -49,6 +55,8 @@ public class TmaPrefsFragment extends PreferenceFragmentCompat { prefs.mAssetReplyDelay, TmaReplyDelay.values())); screen.addPreference(createEnumPref(context, "Login event order", prefs.mLoginEventOrder, TmaLoginEventOrder.values())); + screen.addPreference(createClickPref(context, "Request location perm", + this::requestPermissions)); setPreferenceScreen(screen); } @@ -72,4 +80,25 @@ public class TmaPrefsFragment extends PreferenceFragmentCompat { prefWidget.setEntryValues(entryValues); return prefWidget; } + + private Preference createClickPref(Context context, String title, Consumer<Context> runnable) { + Preference prefWidget = new Preference(context); + prefWidget.setTitle(title); + prefWidget.setOnPreferenceClickListener(pref -> { + runnable.accept(context); + return true; + }); + return prefWidget; + } + + private void requestPermissions(Context context) { + if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + Toast.makeText(context, "Location permission already granted", Toast.LENGTH_SHORT) + .show(); + } else { + ((Activity) context).requestPermissions( + new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, 1); + } + } } |