From 3883053551a1dac316b6ec3087b1cde492cb50e9 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 18 Mar 2019 03:40:34 +0900 Subject: [PATCH] Make compatible with SDK 29 (#34) --- app/build.gradle | 16 +-- app/src/main/AndroidManifest.xml | 16 +-- .../apis/app/AlarmService_Service.java | 19 ++-- .../android/apis/app/ForegroundService.java | 102 +++++++++++------- .../android/apis/app/IncomingMessage.java | 77 ++++++++----- .../appium/android/apis/app/LocalService.java | 17 ++- .../android/apis/app/MessengerService.java | 17 ++- .../android/apis/app/NotifyingService.java | 26 ++--- .../android/apis/app/RemoteService.java | 20 ++-- .../apis/app/ServiceStartArguments.java | 21 ++-- .../apis/app/StatusBarNotifications.java | 94 ++++++++++------ .../android/apis/graphics/BitmapMesh.java | 6 +- .../appium/android/apis/graphics/Layers.java | 21 ++-- .../android/apis/graphics/TouchPaint.java | 2 +- .../android/apis/graphics/Xfermodes.java | 20 +--- .../io/appium/android/apis/os/Sensors.java | 8 +- .../io/appium/android/apis/view/GameView.java | 2 +- app/src/main/res/values/styles.xml | 4 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 20 files changed, 278 insertions(+), 216 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e1ea29f..c0c1277 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,21 +1,25 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { applicationId "io.appium.android.apis" - minSdkVersion 4 - targetSdkVersion 19 + minSdkVersion 17 + targetSdkVersion 28 - versionCode 2 - versionName '3.0.0' + versionCode 17 + versionName '4.0.0' testApplicationId "io.appium.android.apis.test" testInstrumentationRunner ".app.LocalSampleInstrumentation" } + dependencies { + implementation 'androidx.appcompat:appcompat:1.0.2' + } + buildTypes { release { minifyEnabled false diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eab1e35..27a8059 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ own application, the package name must be changed from "com.example.*" to come from a domain that you own or have control over. --> @@ -36,19 +37,20 @@ - - - + diff --git a/app/src/main/java/io/appium/android/apis/app/AlarmService_Service.java b/app/src/main/java/io/appium/android/apis/app/AlarmService_Service.java index 56ac594..856a023 100644 --- a/app/src/main/java/io/appium/android/apis/app/AlarmService_Service.java +++ b/app/src/main/java/io/appium/android/apis/app/AlarmService_Service.java @@ -18,12 +18,14 @@ // Need the following import to get access to the app resources, since this // class is in a sub-package. +import androidx.core.app.NotificationCompat; import io.appium.android.apis.R; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; @@ -98,19 +100,18 @@ public IBinder onBind(Intent intent) { private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.alarm_service_started); - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AlarmService.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.alarm_service_label), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.alarm_service_label)) // the label of the entry + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); // Send the notification. // We use a layout id because it is a unique number. We use it later to cancel. mNM.notify(R.string.alarm_service_started, notification); diff --git a/app/src/main/java/io/appium/android/apis/app/ForegroundService.java b/app/src/main/java/io/appium/android/apis/app/ForegroundService.java index 2805e59..ae4c814 100644 --- a/app/src/main/java/io/appium/android/apis/app/ForegroundService.java +++ b/app/src/main/java/io/appium/android/apis/app/ForegroundService.java @@ -16,14 +16,18 @@ package io.appium.android.apis.app; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; +import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.PowerManager; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; @@ -46,8 +50,18 @@ */ public class ForegroundService extends Service { static final String ACTION_FOREGROUND = "io.appium.android.apis.FOREGROUND"; + static final String ACTION_FOREGROUND_WAKELOCK = "io.appium.android.apis.FOREGROUND_WAKELOCK"; static final String ACTION_BACKGROUND = "io.appium.android.apis.BACKGROUND"; - + static final String ACTION_BACKGROUND_WAKELOCK = "io.appium.android.apis.BACKGROUND_WAKELOCK"; + + private PowerManager.WakeLock mWakeLock; + private Handler mHandler = new Handler(); + private Runnable mPulser = new Runnable() { + @Override public void run() { + Log.i("ForegroundService", "PULSE!"); + mHandler.postDelayed(this, 5*1000); + } + }; private static final Class[] mSetForegroundSignature = new Class[] { boolean.class}; @@ -55,7 +69,7 @@ public class ForegroundService extends Service { int.class, Notification.class}; private static final Class[] mStopForegroundSignature = new Class[] { boolean.class}; - + private NotificationManager mNM; private Method mSetForeground; private Method mStartForeground; @@ -63,7 +77,7 @@ public class ForegroundService extends Service { private Object[] mSetForegroundArgs = new Object[1]; private Object[] mStartForegroundArgs = new Object[2]; private Object[] mStopForegroundArgs = new Object[1]; - + void invokeMethod(Method method, Object[] args) { try { method.invoke(this, args); @@ -75,7 +89,7 @@ void invokeMethod(Method method, Object[] args) { Log.w("ApiDemos", "Unable to invoke method", e); } } - + /** * This is a wrapper around the new startForeground method, using the older * APIs if it is not available. @@ -88,13 +102,13 @@ void startForegroundCompat(int id, Notification notification) { invokeMethod(mStartForeground, mStartForegroundArgs); return; } - + // Fall back on the old API. mSetForegroundArgs[0] = Boolean.TRUE; invokeMethod(mSetForeground, mSetForegroundArgs); mNM.notify(id, notification); } - + /** * This is a wrapper around the new stopForeground method, using the older * APIs if it is not available. @@ -106,14 +120,14 @@ void stopForegroundCompat(int id) { invokeMethod(mStopForeground, mStopForegroundArgs); return; } - + // Fall back on the old API. Note to cancel BEFORE changing the // foreground state, since we could be killed at that point. mNM.cancel(id); mSetForegroundArgs[0] = Boolean.FALSE; invokeMethod(mSetForeground, mSetForegroundArgs); } - + @Override public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); @@ -144,57 +158,65 @@ public void onDestroy() { - // This is the old onStart method that will be called on the pre-2.0 - // platform. On 2.0 or later we override onStartCommand() so this - // method will not be called. - @Override - public void onStart(Intent intent, int startId) { - handleCommand(intent); - } - + @SuppressLint("InvalidWakeLockTag") @Override public int onStartCommand(Intent intent, int flags, int startId) { - handleCommand(intent); - // We want this service to continue running until it is explicitly - // stopped, so return sticky. - return START_STICKY; - } - - - void handleCommand(Intent intent) { - if (ACTION_FOREGROUND.equals(intent.getAction())) { + if (ACTION_FOREGROUND.equals(intent.getAction()) + || ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction())) { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.foreground_service_started); - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.local_service_label), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.alarm_service_label)) // the label + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when clicked + .build(); startForegroundCompat(R.string.foreground_service_started, notification); - - } else if (ACTION_BACKGROUND.equals(intent.getAction())) { + } else if (ACTION_BACKGROUND.equals(intent.getAction()) + || ACTION_BACKGROUND_WAKELOCK.equals(intent.getAction())) { stopForegroundCompat(R.string.foreground_service_started); } + if (ACTION_FOREGROUND_WAKELOCK.equals(intent.getAction()) + || ACTION_BACKGROUND_WAKELOCK.equals(intent.getAction())) { + if (mWakeLock == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mWakeLock = getSystemService(PowerManager.class).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "wake-service"); + mWakeLock.acquire(); + } + } else { + releaseWakeLock(); + } + } + mHandler.removeCallbacks(mPulser); + mPulser.run(); + // We want this service to continue running until it is explicitly + // stopped, so return sticky. + return START_STICKY; } - + + void releaseWakeLock() { + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + } + @Override public IBinder onBind(Intent intent) { return null; } - + // ---------------------------------------------------------------------- /** *

Example of explicitly starting and stopping the {@link ForegroundService}. - * + * *

Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ diff --git a/app/src/main/java/io/appium/android/apis/app/IncomingMessage.java b/app/src/main/java/io/appium/android/apis/app/IncomingMessage.java index eaf1653..0e02531 100644 --- a/app/src/main/java/io/appium/android/apis/app/IncomingMessage.java +++ b/app/src/main/java/io/appium/android/apis/app/IncomingMessage.java @@ -18,15 +18,19 @@ import java.util.Random; +import androidx.annotation.RequiresApi; import io.appium.android.apis.R; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Button; @@ -54,6 +58,8 @@ public void onClick(View v) { showInterstitialNotification(); } }); + + createChannel(); } @@ -94,6 +100,23 @@ static Intent[] makeMessageIntentStack(Context context, CharSequence from, } + private static final String CHANNEL_ID = "main_channel"; + private static final String CHANNEL_NAME = "Sample App"; + private static final String CHANNEL_DESCRIPTION = "Incoming Message"; + + @RequiresApi(Build.VERSION_CODES.O) + private void createChannel() { + NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE); + if (mNotificationManager == null) { + return; + } + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT); + mChannel.setDescription(CHANNEL_DESCRIPTION); + mChannel.setShowBadge(true); + mChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + mNotificationManager.createNotificationChannel(mChannel); + } + /** * The notification is the icon and associated expanded entry in the * status bar. @@ -101,7 +124,6 @@ static Intent[] makeMessageIntentStack(Context context, CharSequence from, void showAppNotification() { // look up the notification manager service NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - // The details of our fake message CharSequence from = "Joe"; CharSequence message; @@ -110,39 +132,42 @@ void showAppNotification() { case 1: message = "im nearby u"; break; default: message = "kthx. meet u for dinner. cul8r"; break; } - // The PendingIntent to launch our activity if the user selects this // notification. Note the use of FLAG_CANCEL_CURRENT so that, if there // is already an active matching pending intent, cancel it and replace // it with the new array of Intents. PendingIntent contentIntent = PendingIntent.getActivities(this, 0, makeMessageIntentStack(this, from, message), PendingIntent.FLAG_CANCEL_CURRENT); - // The ticker text, this uses a formatted string so our message could be localized String tickerText = getString(R.string.imcoming_message_ticker_text, message); - - // construct the Notification object. - Notification notif = new Notification(R.drawable.stat_sample, tickerText, - System.currentTimeMillis()); - // Set the info for the views that show in the notification panel. - notif.setLatestEventInfo(this, from, message, contentIntent); + Notification.Builder notifBuilder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notifBuilder = new Notification.Builder(this, CHANNEL_ID); + } else { + notifBuilder = new Notification.Builder(this); + } + + notifBuilder.setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(tickerText) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(from) // the label of the entry + .setContentText(message) // the contents of the entry + .setContentIntent(contentIntent); // The intent to send when the entry is clicked // We'll have this notification do the default sound, vibration, and led. // Note that if you want any of these behaviors, you should always have // a preference for the user to turn them off. - notif.defaults = Notification.DEFAULT_ALL; - + notifBuilder.setDefaults(Notification.DEFAULT_ALL); // Note that we use R.layout.incoming_message_panel as the ID for // the notification. It could be any integer you want, but we use // the convention of using a resource id for a string related to // the notification. It will always be a unique number within your // application. - nm.notify(R.string.imcoming_message_ticker_text, notif); + nm.notify(R.string.imcoming_message_ticker_text, notifBuilder.build()); } - /** * The notification is the icon and associated expanded entry in the * status bar. @@ -150,7 +175,6 @@ void showAppNotification() { void showInterstitialNotification() { // look up the notification manager service NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - // The details of our fake message CharSequence from = "Dianne"; CharSequence message; @@ -159,7 +183,6 @@ void showInterstitialNotification() { case 1: message = "how about thai down the block?"; break; default: message = "meet u soon. dont b late!"; break; } - // The PendingIntent to launch our activity if the user selects this // notification. Note the use of FLAG_CANCEL_CURRENT so that, if there // is already an active matching pending intent, cancel it and replace @@ -174,24 +197,30 @@ void showInterstitialNotification() { // The ticker text, this uses a formatted string so our message could be localized String tickerText = getString(R.string.imcoming_message_ticker_text, message); - // construct the Notification object. - Notification notif = new Notification(R.drawable.stat_sample, tickerText, - System.currentTimeMillis()); + Notification.Builder notifBuilder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notifBuilder = new Notification.Builder(this, CHANNEL_ID); + } else { + notifBuilder = new Notification.Builder(this); + } // Set the info for the views that show in the notification panel. - notif.setLatestEventInfo(this, from, message, contentIntent); - + notifBuilder + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(tickerText) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(from) // the label of the entry + .setContentText(message) // the contents of the entry + .setContentIntent(contentIntent); // The intent to send when the entry is clicked // We'll have this notification do the default sound, vibration, and led. // Note that if you want any of these behaviors, you should always have // a preference for the user to turn them off. - notif.defaults = Notification.DEFAULT_ALL; - + notifBuilder.setDefaults(Notification.DEFAULT_ALL); // Note that we use R.layout.incoming_message_panel as the ID for // the notification. It could be any integer you want, but we use // the convention of using a resource id for a string related to // the notification. It will always be a unique number within your // application. - nm.notify(R.string.imcoming_message_ticker_text, notif); + nm.notify(R.string.imcoming_message_ticker_text, notifBuilder.build()); } - } diff --git a/app/src/main/java/io/appium/android/apis/app/LocalService.java b/app/src/main/java/io/appium/android/apis/app/LocalService.java index e5213f1..3e5a38e 100644 --- a/app/src/main/java/io/appium/android/apis/app/LocalService.java +++ b/app/src/main/java/io/appium/android/apis/app/LocalService.java @@ -100,19 +100,18 @@ public IBinder onBind(Intent intent) { private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.local_service_started); - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, LocalServiceActivities.Controller.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.local_service_label), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.local_service_label)) // the label of the entry + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); // Send the notification. mNM.notify(NOTIFICATION, notification); } diff --git a/app/src/main/java/io/appium/android/apis/app/MessengerService.java b/app/src/main/java/io/appium/android/apis/app/MessengerService.java index a2af208..d5c2bd4 100644 --- a/app/src/main/java/io/appium/android/apis/app/MessengerService.java +++ b/app/src/main/java/io/appium/android/apis/app/MessengerService.java @@ -148,19 +148,18 @@ public IBinder onBind(Intent intent) { private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.remote_service_started); - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.remote_service_label), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.local_service_label)) // the label of the entry + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. mNM.notify(R.string.remote_service_started, notification); diff --git a/app/src/main/java/io/appium/android/apis/app/NotifyingService.java b/app/src/main/java/io/appium/android/apis/app/NotifyingService.java index 3306292..bb370b2 100644 --- a/app/src/main/java/io/appium/android/apis/app/NotifyingService.java +++ b/app/src/main/java/io/appium/android/apis/app/NotifyingService.java @@ -89,26 +89,26 @@ public void run() { public IBinder onBind(Intent intent) { return mBinder; } - + private void showNotification(int moodId, int textId) { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(textId); - - // Set the icon, scrolling text and timestamp. - // Note that in this example, we pass null for tickerText. We update the icon enough that - // it is distracting to show the ticker text every time it changes. We strongly suggest - // that you do this as well. (Think of of the "New hardware found" or "Network connection - // changed" messages that always pop up) - Notification notification = new Notification(moodId, null, System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, NotifyingController.class), 0); - + // Set the icon and timestamp. + // Note that in this example, we do not set the tickerText. We update the icon enough that + // it is distracting to show the ticker text every time it changes. We strongly suggest + // that you do this as well. (Think of of the "New hardware found" or "Network connection + // changed" messages that always pop up) // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.status_bar_notifications_mood_title), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(moodId) + .setWhen(System.currentTimeMillis()) + .setContentTitle(getText(R.string.status_bar_notifications_mood_title)) + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); // Send the notification. // We use a layout id because it is a unique number. We use it later to cancel. mNM.notify(MOOD_NOTIFICATIONS, notification); diff --git a/app/src/main/java/io/appium/android/apis/app/RemoteService.java b/app/src/main/java/io/appium/android/apis/app/RemoteService.java index 959fb82..ee78836 100644 --- a/app/src/main/java/io/appium/android/apis/app/RemoteService.java +++ b/app/src/main/java/io/appium/android/apis/app/RemoteService.java @@ -176,30 +176,30 @@ public void onTaskRemoved(Intent rootIntent) { } }; + /** * Show a notification while this service is running. */ private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.remote_service_started); - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.remote_service_label), - text, contentIntent); - + Notification notification = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.remote_service_label)) // the label of the entry + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. mNM.notify(R.string.remote_service_started, notification); } - + // ---------------------------------------------------------------------- /** diff --git a/app/src/main/java/io/appium/android/apis/app/ServiceStartArguments.java b/app/src/main/java/io/appium/android/apis/app/ServiceStartArguments.java index 1bd8eb3..4ff4ac9 100644 --- a/app/src/main/java/io/appium/android/apis/app/ServiceStartArguments.java +++ b/app/src/main/java/io/appium/android/apis/app/ServiceStartArguments.java @@ -174,24 +174,23 @@ public IBinder onBind(Intent intent) { * Show a notification while this service is running. */ private void showNotification(String text) { - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(R.drawable.stat_sample, text, - System.currentTimeMillis()); - // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); - // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.service_start_arguments_label), - text, contentIntent); - + Notification.Builder noteBuilder = new Notification.Builder(this) + .setSmallIcon(R.drawable.stat_sample) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.service_start_arguments_label)) // the label + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent); // The intent to send when the entry is clicked // We show this for as long as our service is processing a command. - notification.flags |= Notification.FLAG_ONGOING_EVENT; - + noteBuilder.setOngoing(true); + // Send the notification. // We use a string id because it is a unique number. We use it later to cancel. - mNM.notify(R.string.service_created, notification); + mNM.notify(R.string.service_created, noteBuilder.build()); } private void hideNotification() { diff --git a/app/src/main/java/io/appium/android/apis/app/StatusBarNotifications.java b/app/src/main/java/io/appium/android/apis/app/StatusBarNotifications.java index bd18ae1..268690a 100644 --- a/app/src/main/java/io/appium/android/apis/app/StatusBarNotifications.java +++ b/app/src/main/java/io/appium/android/apis/app/StatusBarNotifications.java @@ -16,14 +16,17 @@ package io.appium.android.apis.app; +import androidx.annotation.RequiresApi; import io.appium.android.apis.R; import android.app.Activity; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Button; @@ -143,6 +146,8 @@ public void onClick(View v) { mNotificationManager.cancel(R.layout.status_bar_notifications); } }); + + createChannel(); } private PendingIntent makeMoodIntent(int moodId) { @@ -189,25 +194,51 @@ private PendingIntent makeDefaultIntent() { return contentIntent; } + private static final String CHANNEL_ID = "main_channel"; + private static final String CHANNEL_NAME = "Sample App"; + private static final String CHANNEL_DESCRIPTION = "Status Bar Notifications"; + + + @RequiresApi(Build.VERSION_CODES.O) + private void createChannel() { + NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(NOTIFICATION_SERVICE); + if (mNotificationManager == null) { + return; + } + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT); + mChannel.setDescription(CHANNEL_DESCRIPTION); + mChannel.setShowBadge(true); + mChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + mNotificationManager.createNotificationChannel(mChannel); + } private void setMood(int moodId, int textId, boolean showTicker) { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(textId); - - // choose the ticker text - String tickerText = showTicker ? getString(textId) : null; - - // Set the icon, scrolling text and timestamp - Notification notification = new Notification(moodId, tickerText, - System.currentTimeMillis()); - + // In this sample, we'll use this text for the title of the notification + CharSequence title = getText(R.string.status_bar_notifications_mood_title); // Set the info for the views that show in the notification panel. - notification.setLatestEventInfo(this, getText(R.string.status_bar_notifications_mood_title), - text, makeMoodIntent(moodId)); + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder = new Notification.Builder(this, CHANNEL_ID); + } else { + builder = new Notification.Builder(this); + } + + Notification.Builder notifBuidler = builder // the context to use + .setSmallIcon(moodId) // the status icon + .setWhen(System.currentTimeMillis()) // the timestamp for the notification + .setContentTitle(title) // the title for the notification + .setContentText(text) // the details to display in the notification + .setContentIntent(makeMoodIntent(moodId)); // The intent to send clicked + if (showTicker) { + // include the ticker text + notifBuidler.setTicker(getString(textId)); + } // Send the notification. // We use a layout id because it is a unique number. We use it later to cancel. - mNotificationManager.notify(MOOD_NOTIFICATIONS, notification); + mNotificationManager.notify(MOOD_NOTIFICATIONS, notifBuidler.build()); } private void setMoodView(int moodId, int textId) { @@ -237,34 +268,35 @@ private void setMoodView(int moodId, int textId) { // notification mNotificationManager.notify(MOOD_NOTIFICATIONS, notif); } - + private void setDefault(int defaults) { - - // This method sets the defaults on the notification before posting it. - // This is who should be launched if the user selects our notification. PendingIntent contentIntent = makeDefaultIntent(); - // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.status_bar_notifications_happy_message); + // In this sample, we'll use this text for the title of the notification + CharSequence title = getText(R.string.status_bar_notifications_mood_title); - final Notification notification = new Notification( - R.drawable.stat_happy, // the icon for the status bar - text, // the text to display in the ticker - System.currentTimeMillis()); // the timestamp for the notification - - notification.setLatestEventInfo( - this, // the context to use - getText(R.string.status_bar_notifications_mood_title), - // the title for the notification - text, // the details to display in the notification - contentIntent); // the contentIntent (see above) + Notification.Builder notifBuilder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notifBuilder = new Notification.Builder(this, CHANNEL_ID); + } else { + notifBuilder = new Notification.Builder(this); + } - notification.defaults = defaults; - + // Set the info for the views that show in the notification panel. + Notification notification = notifBuilder // the context to use + .setSmallIcon(R.drawable.stat_happy) // the status icon + .setTicker(text) // the text to display in the ticker + .setWhen(System.currentTimeMillis()) // the timestamp for the notification + .setContentTitle(title) // the title for the notification + .setContentText(text) // the details to display in the notification + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .setDefaults(defaults) + .build(); mNotificationManager.notify( MOOD_NOTIFICATIONS, // we use a string id because it is a unique - // number. we use it later to cancel the notification + // number. we use it later to cancel the notification notification); - } + } } diff --git a/app/src/main/java/io/appium/android/apis/graphics/BitmapMesh.java b/app/src/main/java/io/appium/android/apis/graphics/BitmapMesh.java index 26358e9..d9079ae 100644 --- a/app/src/main/java/io/appium/android/apis/graphics/BitmapMesh.java +++ b/app/src/main/java/io/appium/android/apis/graphics/BitmapMesh.java @@ -92,12 +92,10 @@ private void warp(float cx, float cy) { float dx = cx - x; float dy = cy - y; float dd = dx*dx + dy*dy; - float d = FloatMath.sqrt(dd); + float d = (float) Math.sqrt(dd); float pull = K / (dd + 0.000001f); - pull /= (d + 0.000001f); - // android.util.Log.d("skia", "index " + i + " dist=" + d + " pull=" + pull); - + // android.util.Log.d("skia", "index " + i + " dist=" + d + " pull=" + pull); if (pull >= 1) { dst[i+0] = cx; dst[i+1] = cy; diff --git a/app/src/main/java/io/appium/android/apis/graphics/Layers.java b/app/src/main/java/io/appium/android/apis/graphics/Layers.java index 1f2083b..408f2eb 100644 --- a/app/src/main/java/io/appium/android/apis/graphics/Layers.java +++ b/app/src/main/java/io/appium/android/apis/graphics/Layers.java @@ -18,46 +18,37 @@ import android.content.Context; import android.graphics.*; +import android.os.Build; import android.os.Bundle; import android.view.*; public class Layers extends GraphicsActivity { - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } - private static class SampleView extends View { - private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG | - Canvas.CLIP_SAVE_FLAG | - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | - Canvas.FULL_COLOR_LAYER_SAVE_FLAG | - Canvas.CLIP_TO_LAYER_SAVE_FLAG; - private Paint mPaint; public SampleView(Context context) { super(context); setFocusable(true); - mPaint = new Paint(); mPaint.setAntiAlias(true); } - @Override protected void onDraw(Canvas canvas) { + @Override + protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); - canvas.translate(10, 10); - - canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS); - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + canvas.saveLayerAlpha(0, 0, 200, 200, 0x88); + } mPaint.setColor(Color.RED); canvas.drawCircle(75, 75, 75, mPaint); mPaint.setColor(Color.BLUE); canvas.drawCircle(125, 125, 75, mPaint); - canvas.restore(); } } diff --git a/app/src/main/java/io/appium/android/apis/graphics/TouchPaint.java b/app/src/main/java/io/appium/android/apis/graphics/TouchPaint.java index b9d836d..6cb6efe 100644 --- a/app/src/main/java/io/appium/android/apis/graphics/TouchPaint.java +++ b/app/src/main/java/io/appium/android/apis/graphics/TouchPaint.java @@ -457,7 +457,7 @@ private void paint(PaintMode mode, float x, float y, float pressure, private final RectF mReusableOvalRect = new RectF(); private void drawOval(Canvas canvas, float x, float y, float major, float minor, float orientation, Paint paint) { - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); canvas.rotate((float) (orientation * 180 / Math.PI), x, y); mReusableOvalRect.left = x - minor / 2; mReusableOvalRect.right = x + minor / 2; diff --git a/app/src/main/java/io/appium/android/apis/graphics/Xfermodes.java b/app/src/main/java/io/appium/android/apis/graphics/Xfermodes.java index 0542181..85986fa 100644 --- a/app/src/main/java/io/appium/android/apis/graphics/Xfermodes.java +++ b/app/src/main/java/io/appium/android/apis/graphics/Xfermodes.java @@ -116,15 +116,11 @@ public SampleView(Context context) { @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); - Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG); labelP.setTextAlign(Paint.Align.CENTER); - Paint paint = new Paint(); paint.setFilterBitmap(false); - canvas.translate(15, 35); - int x = 0; int y = 0; for (int i = 0; i < sModes.length; i++) { @@ -132,33 +128,23 @@ public SampleView(Context context) { paint.setStyle(Paint.Style.STROKE); paint.setShader(null); canvas.drawRect(x - 0.5f, y - 0.5f, - x + W + 0.5f, y + H + 0.5f, paint); - + x + W + 0.5f, y + H + 0.5f, paint); // draw the checker-board pattern paint.setStyle(Paint.Style.FILL); paint.setShader(mBG); canvas.drawRect(x, y, x + W, y + H, paint); - // draw the src/dst example into our offscreen bitmap - int sc = canvas.saveLayer(x, y, x + W, y + H, null, - Canvas.MATRIX_SAVE_FLAG | - Canvas.CLIP_SAVE_FLAG | - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | - Canvas.FULL_COLOR_LAYER_SAVE_FLAG | - Canvas.CLIP_TO_LAYER_SAVE_FLAG); + int sc = canvas.saveLayer(x, y, x + W, y + H, null); canvas.translate(x, y); canvas.drawBitmap(mDstB, 0, 0, paint); paint.setXfermode(sModes[i]); canvas.drawBitmap(mSrcB, 0, 0, paint); paint.setXfermode(null); canvas.restoreToCount(sc); - // draw the label canvas.drawText(sLabels[i], - x + W/2, y - labelP.getTextSize()/2, labelP); - + x + W/2, y - labelP.getTextSize()/2, labelP); x += W + 10; - // wrap around when we've drawn enough for one row if ((i % ROW_MAX) == ROW_MAX - 1) { x = 0; diff --git a/app/src/main/java/io/appium/android/apis/os/Sensors.java b/app/src/main/java/io/appium/android/apis/os/Sensors.java index a69aeec..b246802 100644 --- a/app/src/main/java/io/appium/android/apis/os/Sensors.java +++ b/app/src/main/java/io/appium/android/apis/os/Sensors.java @@ -131,9 +131,9 @@ protected void onDraw(Canvas canvas) { float w = w0 - 32; float x = w0*0.5f; for (int i=0 ; i<3 ; i++) { - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); canvas.translate(x, w*0.5f + 4.0f); - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); paint.setColor(outer); canvas.scale(w, w); canvas.drawOval(mRect, paint); @@ -150,9 +150,9 @@ protected void onDraw(Canvas canvas) { float h = h0 - 32; float y = h0*0.5f; for (int i=0 ; i<3 ; i++) { - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); canvas.translate(mWidth - (h*0.5f + 4.0f), y); - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); paint.setColor(outer); canvas.scale(h, h); canvas.drawOval(mRect, paint); diff --git a/app/src/main/java/io/appium/android/apis/view/GameView.java b/app/src/main/java/io/appium/android/apis/view/GameView.java index ecf0fd0..c9ba700 100644 --- a/app/src/main/java/io/appium/android/apis/view/GameView.java +++ b/app/src/main/java/io/appium/android/apis/view/GameView.java @@ -690,7 +690,7 @@ public void draw(Canvas canvas) { 255, 63, 255, 63, 0, 255, 0, 0); - canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.save(); canvas.translate(mPositionX, mPositionY); canvas.rotate(mHeadingAngle * TO_DEGREES); canvas.drawPath(mPath, mPaint); diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 49a1c25..75c79de 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -66,14 +66,14 @@ - -