├── settings.gradle
├── A2DP-Connect.png
├── app
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── icon.png
│ │ │ └── icon2.png
│ │ ├── xml
│ │ │ └── widget_info.xml
│ │ ├── layout
│ │ │ ├── main.xml
│ │ │ └── widget_initial_layout.xml
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── a2dp
│ │ │ └── connect2
│ │ │ ├── BtReceiver.java
│ │ │ ├── WidgetProvider.java
│ │ │ ├── Bt_iadl.java
│ │ │ ├── MainActivity.java
│ │ │ ├── RunUpdate.java
│ │ │ └── Connector.java
│ │ ├── aidl
│ │ └── android
│ │ │ └── bluetooth
│ │ │ ├── IBluetooth.aidl
│ │ │ └── IBluetoothA2dp.aidl
│ │ └── AndroidManifest.xml
├── lint.xml
├── build.gradle
└── app.iml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── local.properties
├── .classpath
├── a2dp-connect2a.iml
├── .project
├── README.asciidoc
├── import-summary.txt
├── gradlew.bat
├── .gitignore
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/A2DP-Connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jroal/a2dp-connect2/HEAD/A2DP-Connect.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jroal/a2dp-connect2/HEAD/app/src/main/res/drawable/icon.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jroal/a2dp-connect2/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jroal/a2dp-connect2/HEAD/app/src/main/res/drawable/icon2.png
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | #
6 | #Thu Sep 20 19:05:50 CDT 2018
7 | sdk.dir=K\:\\AndroidSDK
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Feb 17 14:00:22 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/widget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/a2dp-connect2a.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | A2DP Connect
4 | Connect
5 | Invalid Device
6 | Null Device
7 | Connecting
8 | Disconnecting
9 | Bluetooth Device
10 | Device does not have Bluetooth enabled
11 | Bluetooth must be enabled before you can place a widget
12 | Bluetooth not enabled
13 | Interface error
14 | Foreground Channel
15 | Foreground notification channel
16 |
17 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | A2DP_Connect_2
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/BtReceiver.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import android.appwidget.AppWidgetManager;
4 | import android.bluetooth.BluetoothDevice;
5 | import android.content.BroadcastReceiver;
6 | import android.content.ComponentName;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.os.Build;
10 |
11 | import java.util.Objects;
12 |
13 | public class BtReceiver extends BroadcastReceiver {
14 |
15 | @Override
16 | public void onReceive(Context context, Intent intent) {
17 |
18 | Intent intent2 = new Intent(context, RunUpdate.class);
19 |
20 | context.startService(intent2);
21 |
22 | /* if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
23 | context.startService(intent2);
24 | } else {
25 | context.startForegroundService(intent2);
26 | }*/
27 |
28 | // an Intent broadcast.
29 | throw new UnsupportedOperationException("Not yet implemented");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/widget_initial_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/aidl/android/bluetooth/IBluetooth.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.bluetooth;
18 |
19 | import android.bluetooth.BluetoothDevice;
20 |
21 | /**
22 | * System private API for Bluetooth service
23 | *
24 | * {@hide}
25 | */
26 | interface IBluetooth {
27 |
28 | String getRemoteAlias(in BluetoothDevice device);
29 |
30 | boolean setRemoteAlias(in BluetoothDevice device, in String name);
31 | }
32 |
--------------------------------------------------------------------------------
/README.asciidoc:
--------------------------------------------------------------------------------
1 | # a2dp-connect2
2 |
3 | *This separate widget is going away. Instead it is now integrated into link:https://github.com/jroal/a2dpvolume[A2DP Volume] starting with version 2.13.0.0. Become a tester here https://play.google.com/apps/testing/a2dp.Vol and get that app now.* See details of how to use the widget here: https://github.com/jroal/a2dpvolume/wiki/Widgets
4 |
5 | This is a simple widget (not an app) to connect/disconnect from A2DP Bluetooth sinks. That is all it does.
6 |
7 | .To use it:
8 | . Install from Play Store
9 | . Pair your Bluetooth devices using Android settings
10 | . Turn Bluetooth ON
11 | . Place an instance of this widget on your home screen (launcher dependent on how you do this)
12 | . Click it to connect a device, again to disconnect.
13 |
14 | The widget will connect if the device is currently not connected, and disconnect if the device is currently connected.
15 |
16 | Become a Beta tester here: https://play.google.com/apps/testing/a2dp.connect2
17 |
18 | This app is a mess. I have numerous timing and cross thread problems and need a real developer to help me architect this correctly. *I integrated this widget into link:https://github.com/jroal/a2dpvolume[A2DP Volume] instead of having it be a separate executable.*
19 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | // Load keystore
4 | def keystorePropertiesFile = rootProject.file("keystore.properties");
5 | def keystoreProperties = new Properties()
6 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
7 |
8 |
9 | android {
10 |
11 | signingConfigs {
12 | release {
13 | keyAlias keystoreProperties['keyAlias']
14 | keyPassword keystoreProperties['keyPassword']
15 | storeFile file(keystoreProperties['storeFile'])
16 | storePassword keystoreProperties['storePassword']
17 | }
18 | }
19 | compileSdkVersion 28
20 | buildToolsVersion '28.0.3'
21 |
22 | defaultConfig {
23 | applicationId "a2dp.connect2"
24 | minSdkVersion 19
25 | targetSdkVersion 28
26 | signingConfig signingConfigs.release
27 | }
28 |
29 | buildTypes {
30 | release {
31 | signingConfig signingConfigs.release
32 | zipAlignEnabled true
33 | minifyEnabled false
34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
35 | }
36 | }
37 |
38 | dependencies {
39 | implementation 'com.android.support:support-v4:28.0.0'
40 | implementation 'com.android.support:appcompat-v7:28.0.0'
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * .hgignore
11 | * .hgignore-415cc148
12 | * .idea\
13 | * .idea\.name
14 | * .idea\a2dp-connect2.iml
15 | * .idea\codeStyles\
16 | * .idea\codeStyles\Project.xml
17 | * .idea\compiler.xml
18 | * .idea\copyright\
19 | * .idea\copyright\profiles_settings.xml
20 | * .idea\encodings.xml
21 | * .idea\misc.xml
22 | * .idea\modules.xml
23 | * .idea\vcs.xml
24 | * .idea\workspace.xml
25 | * A2DP-Connect.png
26 | * README.asciidoc
27 | * projectFilesBackup\
28 | * projectFilesBackup\.idea\
29 | * projectFilesBackup\.idea\workspace.xml
30 |
31 | Moved Files:
32 | ------------
33 | Android Gradle projects use a different directory structure than ADT
34 | Eclipse projects. Here's how the projects were restructured:
35 |
36 | * AndroidManifest.xml => app\src\main\AndroidManifest.xml
37 | * lint.xml => app\lint.xml
38 | * res\ => app\src\main\res\
39 | * src\ => app\src\main\java\
40 | * src\android\bluetooth\IBluetooth.aidl => app\src\main\aidl\android\bluetooth\IBluetooth.aidl
41 | * src\android\bluetooth\IBluetoothA2dp.aidl => app\src\main\aidl\android\bluetooth\IBluetoothA2dp.aidl
42 |
43 | Next Steps:
44 | -----------
45 | You can now build the project. The Gradle project needs network
46 | connectivity to download dependencies.
47 |
48 | Bugs:
49 | -----
50 | If for some reason your project does not build, and you determine that
51 | it is due to a bug or limitation of the Eclipse to Gradle importer,
52 | please file a bug at http://b.android.com with category
53 | Component-Tools.
54 |
55 | (This import summary is for your information only, and can be deleted
56 | after import once you are satisfied with the results.)
57 |
--------------------------------------------------------------------------------
/app/src/main/aidl/android/bluetooth/IBluetoothA2dp.aidl:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.bluetooth;
18 |
19 | import android.bluetooth.BluetoothDevice;
20 |
21 | /**
22 | * System private API for Bluetooth A2DP service
23 | *
24 | * {@hide}
25 | */
26 | interface IBluetoothA2dp {
27 | boolean connectSink(in BluetoothDevice device); // Pre API 11 only
28 | boolean disconnectSink(in BluetoothDevice device); // Pre API 11 only
29 | boolean connect(in BluetoothDevice device); // API 11 and up only
30 | boolean disconnect(in BluetoothDevice device); // API 11 and up only
31 | boolean suspendSink(in BluetoothDevice device); // all
32 | boolean resumeSink(in BluetoothDevice device); // all
33 | BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports, pre API 11 only
34 | BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports,
35 | int getSinkState(in BluetoothDevice device); // pre API 11 only
36 | int getConnectionState(in BluetoothDevice device); // API 11 and up
37 | boolean setSinkPriority(in BluetoothDevice device, int priority); // Pre API 11 only
38 | boolean setPriority(in BluetoothDevice device, int priority); // API 11 and up only
39 | int getPriority(in BluetoothDevice device); // API 11 and up only
40 | int getSinkPriority(in BluetoothDevice device); // Pre API 11 only
41 | boolean isA2dpPlaying(in BluetoothDevice device); // API 11 and up only
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/WidgetProvider.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import android.app.PendingIntent;
4 | import android.appwidget.AppWidgetManager;
5 | import android.appwidget.AppWidgetProvider;
6 | import android.bluetooth.BluetoothAdapter;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.os.Build;
11 | import android.util.Log;
12 | import android.widget.RemoteViews;
13 |
14 |
15 |
16 | public class WidgetProvider extends AppWidgetProvider {
17 |
18 | private static final String LOG_TAG = "Widget_Provider";
19 | private String PREFS = "bluetoothlauncher";
20 |
21 | /*
22 | * (non-Javadoc)
23 | *
24 | * @see
25 | * android.appwidget.AppWidgetProvider#onEnabled(android.content.Context)
26 | */
27 | @Override
28 | public void onEnabled(Context context) {
29 |
30 | Log.i(LOG_TAG, "Widget enabled");
31 |
32 | super.onEnabled(context);
33 | }
34 |
35 | @Override
36 | public void onReceive(Context context, Intent intent) {
37 |
38 | Log.i(LOG_TAG, "Widget receive");
39 | // Toast.makeText(context, "onRecieve", Toast.LENGTH_LONG).show();
40 | //String address = intent.getStringExtra()
41 | //isDeviceConnected()
42 | super.onReceive(context, intent);
43 | }
44 |
45 |
46 | @Override
47 | public void onUpdate(Context context, AppWidgetManager appWidgetManager,
48 | int[] appWidgetIds) {
49 |
50 | BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
51 |
52 | SharedPreferences preferences = context.getSharedPreferences(PREFS, 0);
53 | // Perform this loop procedure for each App Widget that belongs to this
54 | // provider
55 |
56 | for (int appWidgetId : appWidgetIds) {
57 | // Create an Intent to launch
58 | Intent intent = new Intent(context, Connector.class);
59 | intent.putExtra("ID", appWidgetId);
60 | PendingIntent pendingIntent;
61 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
62 | pendingIntent = PendingIntent.getService(context,
63 | appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
64 | } else {
65 | pendingIntent = PendingIntent.getForegroundService(context,
66 | appWidgetId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
67 | }
68 |
69 |
70 | Log.i(LOG_TAG, "Widget ID = " + appWidgetId);
71 | // Get the layout for the App Widget and attach an on-click listener
72 | // to the button
73 |
74 | RemoteViews views = new RemoteViews(context.getPackageName(),
75 | R.layout.widget_initial_layout);
76 | views.setOnClickPendingIntent(R.id.WidgetButton, pendingIntent);
77 | String WidgetId = String.valueOf(appWidgetId);
78 | String bt_mac = preferences.getString(WidgetId, "O");
79 | String dname = preferences.getString(WidgetId + "_name", "Connect " + appWidgetId);
80 | views.setTextViewText(R.id.WidgetButton, dname);
81 |
82 | // Tell the AppWidgetManager to perform an update on the current App
83 | // Widget
84 | appWidgetManager.updateAppWidget(appWidgetId, views);
85 |
86 | }
87 |
88 | super.onUpdate(context, appWidgetManager, appWidgetIds);
89 | }
90 |
91 |
92 |
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/Bt_iadl.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 | import java.lang.reflect.Method;
5 | import java.util.Set;
6 |
7 | import android.bluetooth.BluetoothA2dp;
8 | import android.bluetooth.BluetoothAdapter;
9 | import android.bluetooth.BluetoothDevice;
10 | import android.bluetooth.IBluetooth;
11 | import android.bluetooth.IBluetoothA2dp;
12 | import android.content.ComponentName;
13 | import android.content.Context;
14 | import android.content.Intent;
15 | import android.content.ServiceConnection;
16 | import android.content.pm.PackageManager;
17 | import android.os.IBinder;
18 | import android.os.RemoteException;
19 | import android.util.Log;
20 | import android.widget.Toast;
21 |
22 | public class Bt_iadl {
23 |
24 | public static final String filter_1_string = "a2dp.connect2.Connector.INTERFACE";
25 | public static String NameFilter = "a2dp.connect2.Connector.NAME";
26 | public static IBluetoothA2dp ibta2 = null;
27 | static IBluetooth ibt2 = null;
28 | static String address;
29 | static Context c1;
30 | public static boolean mIsBound = false;
31 | public static boolean m2IsBound = false;
32 | static String LOG_TAG = "Bt-iadl";
33 |
34 |
35 |
36 | static void getNames(Context c1) {
37 |
38 | int i = 0;
39 | BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
40 |
41 | if (mBTA != null) {
42 | Set pairedDevices = mBTA.getBondedDevices();
43 | // If there are paired devices
44 | if (pairedDevices.size() > 0) {
45 | // Loop through paired devices
46 | for (BluetoothDevice device : pairedDevices) {
47 | String dname = device.getName();
48 |
49 | try {
50 | Method m = device.getClass().getMethod("getAliasName");
51 | Object res = m.invoke(device);
52 | if (res != null)
53 | dname = res.toString();
54 | } catch (NoSuchMethodException e) {
55 | e.printStackTrace();
56 | } catch (InvocationTargetException e) {
57 | e.printStackTrace();
58 | } catch (IllegalAccessException e) {
59 | e.printStackTrace();
60 | }
61 |
62 | if (dname == null)
63 | dname = device.getName();
64 | MainActivity.temp[i][0] = dname;
65 | MainActivity.temp[i][1] = device.getAddress();
66 | if (i > 48)
67 | break;
68 | i++;
69 | }
70 | }
71 | }
72 |
73 | Intent intent = new Intent();
74 | intent.setAction(NameFilter);
75 | intent.putExtra("slength", i);
76 | c1.sendBroadcast(intent);
77 | }
78 |
79 |
80 |
81 | private static void sendIntent() {
82 | Intent intent = new Intent();
83 | intent.setAction(filter_1_string);
84 | c1.sendBroadcast(intent);
85 | };
86 |
87 |
88 |
89 |
90 |
91 | public static boolean isDeviceConnected(String btd) {
92 | BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
93 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(btd);
94 |
95 |
96 | int sinkState = 0;
97 | try {
98 | sinkState = ibta2.getConnectionState(device);
99 | } catch (RemoteException e) {
100 | e.printStackTrace();
101 | }
102 | Boolean connected = sinkState == BluetoothA2dp.STATE_CONNECTED || sinkState == BluetoothA2dp.STATE_CONNECTING;
103 |
104 | Boolean result = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
105 | && mBluetoothAdapter.getProfileConnectionState(BluetoothA2dp.A2DP) == BluetoothA2dp.STATE_CONNECTED && connected ;
106 | Log.i(LOG_TAG, "Mac connected " + btd + " - " + result);
107 | return result;
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | .gradle/
11 | *.tmp
12 | *.bak
13 | *.swp
14 | *~.nib
15 | local.properties
16 | .classpath
17 | .settings/
18 | .loadpath
19 | .idea
20 | .gradle
21 | keystore.properties
22 | *.iml
23 | app/release
24 | app/build
25 |
26 |
27 | # External tool builders
28 | .externalToolBuilders/
29 |
30 | # Locally stored "Eclipse launch configurations"
31 | *.launch
32 |
33 | # CDT-specific
34 | .cproject
35 |
36 | # PDT-specific
37 | .buildpath
38 |
39 |
40 | #################
41 | ## Visual Studio
42 | #################
43 |
44 | ## Ignore Visual Studio temporary files, build results, and
45 | ## files generated by popular Visual Studio add-ons.
46 |
47 | # User-specific files
48 | *.suo
49 | *.user
50 | *.sln.docstates
51 |
52 | # Build results
53 |
54 | [Dd]ebug/
55 | [Rr]elease/
56 | x64/
57 | build/
58 | [Bb]in/
59 | [Oo]bj/
60 |
61 | # MSTest test Results
62 | [Tt]est[Rr]esult*/
63 | [Bb]uild[Ll]og.*
64 |
65 | *_i.c
66 | *_p.c
67 | *.ilk
68 | *.meta
69 | *.obj
70 | *.pch
71 | *.pdb
72 | *.pgc
73 | *.pgd
74 | *.rsp
75 | *.sbr
76 | *.tlb
77 | *.tli
78 | *.tlh
79 | *.tmp
80 | *.tmp_proj
81 | *.log
82 | *.vspscc
83 | *.vssscc
84 | .builds
85 | *.pidb
86 | *.log
87 | *.scc
88 |
89 | # Visual C++ cache files
90 | ipch/
91 | *.aps
92 | *.ncb
93 | *.opensdf
94 | *.sdf
95 | *.cachefile
96 |
97 | # Visual Studio profiler
98 | *.psess
99 | *.vsp
100 | *.vspx
101 |
102 | # Guidance Automation Toolkit
103 | *.gpState
104 |
105 | # ReSharper is a .NET coding add-in
106 | _ReSharper*/
107 | *.[Rr]e[Ss]harper
108 |
109 | # TeamCity is a build add-in
110 | _TeamCity*
111 |
112 | # DotCover is a Code Coverage Tool
113 | *.dotCover
114 |
115 | # NCrunch
116 | *.ncrunch*
117 | .*crunch*.local.xml
118 |
119 | # Installshield output folder
120 | [Ee]xpress/
121 |
122 | # DocProject is a documentation generator add-in
123 | DocProject/buildhelp/
124 | DocProject/Help/*.HxT
125 | DocProject/Help/*.HxC
126 | DocProject/Help/*.hhc
127 | DocProject/Help/*.hhk
128 | DocProject/Help/*.hhp
129 | DocProject/Help/Html2
130 | DocProject/Help/html
131 |
132 | # Click-Once directory
133 | publish/
134 |
135 | # Publish Web Output
136 | *.Publish.xml
137 | *.pubxml
138 | *.publishproj
139 |
140 | # NuGet Packages Directory
141 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
142 | #packages/
143 |
144 | # Windows Azure Build Output
145 | csx
146 | *.build.csdef
147 |
148 | # Windows Store app package directory
149 | AppPackages/
150 |
151 | # Others
152 | sql/
153 | *.Cache
154 | ClientBin/
155 | [Ss]tyle[Cc]op.*
156 | ~$*
157 | *~
158 | *.dbmdl
159 | *.[Pp]ublish.xml
160 | *.pfx
161 | *.publishsettings
162 |
163 | # RIA/Silverlight projects
164 | Generated_Code/
165 |
166 | # Backup & report files from converting an old project file to a newer
167 | # Visual Studio version. Backup files are not needed, because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | App_Data/*.mdf
175 | App_Data/*.ldf
176 |
177 | #############
178 | ## Windows detritus
179 | #############
180 |
181 | # Windows image file caches
182 | Thumbs.db
183 | ehthumbs.db
184 |
185 | # Folder config file
186 | Desktop.ini
187 |
188 | # Recycle Bin used on file shares
189 | $RECYCLE.BIN/
190 |
191 | # Mac crap
192 | .DS_Store
193 |
194 |
195 | #############
196 | ## Python
197 | #############
198 |
199 | *.py[cod]
200 |
201 | # Packages
202 | *.egg
203 | *.egg-info
204 | dist/
205 | build/
206 | eggs/
207 | parts/
208 | var/
209 | sdist/
210 | develop-eggs/
211 | .installed.cfg
212 |
213 | # Installer logs
214 | pip-log.txt
215 |
216 | # Unit test / coverage reports
217 | .coverage
218 | .tox
219 |
220 | #Translations
221 | *.mo
222 |
223 | #Mr Developer
224 | .mr.developer.cfg
225 |
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/MainActivity.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.app.PendingIntent;
6 | import android.appwidget.AppWidgetManager;
7 | import android.bluetooth.BluetoothAdapter;
8 | import android.content.BroadcastReceiver;
9 | import android.content.Context;
10 | import android.content.DialogInterface;
11 | import android.content.Intent;
12 | import android.content.IntentFilter;
13 | import android.content.SharedPreferences;
14 | import android.os.Bundle;
15 | import android.widget.RemoteViews;
16 | import android.widget.Toast;
17 |
18 | public class MainActivity extends Activity {
19 |
20 | private String PREFS = "bluetoothlauncher";
21 | static boolean receiver_registered = false;
22 |
23 | // int w_id = 0;
24 | int mAppWidgetId;
25 | public Context application;
26 | String dname;
27 | public final static String temp[][] = new String[50][2];
28 |
29 | @Override
30 | protected void onDestroy() {
31 | try {
32 | if (receiver_registered) {
33 | application.unregisterReceiver(receiver1);
34 | receiver_registered = false;
35 | }
36 | //a2dp.connect2.Bt_iadl.doUnbindService2(application);
37 | } catch (Exception e) {
38 | // TODO Auto-generated catch block
39 | e.printStackTrace();
40 | }
41 | super.onDestroy();
42 | }
43 |
44 | @Override
45 | protected void onStart() {
46 | if (!receiver_registered) {
47 | IntentFilter f1 = new IntentFilter(Bt_iadl.NameFilter);
48 | application.registerReceiver(receiver1, f1);
49 | receiver_registered = true;
50 | }
51 | super.onStart();
52 | }
53 |
54 | @Override
55 | protected void onStop() {
56 |
57 | super.onStop();
58 | }
59 |
60 |
61 | /** Called when the activity is first created. */
62 | @Override
63 | public void onCreate(Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | setContentView(R.layout.main);
66 | application = getApplication();
67 | Intent intent = getIntent();
68 | Bundle extras = intent.getExtras();
69 | if (extras != null) {
70 | mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
71 | AppWidgetManager.INVALID_APPWIDGET_ID);
72 | }
73 |
74 | config(mAppWidgetId);
75 | }
76 |
77 | public void config(final int id) {
78 |
79 | BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
80 | if (mBTA == null) {
81 | Toast.makeText(this, R.string.NoBluetooth, Toast.LENGTH_LONG)
82 | .show();
83 | return;
84 | }
85 | // If Bluetooth is not yet enabled, enable it
86 | if (!mBTA.isEnabled()) {
87 | Toast.makeText(application, R.string.NeedEnable, Toast.LENGTH_LONG)
88 | .show();
89 | this.finish();
90 | /*
91 | * Intent enableBluetooth = new Intent(
92 | * BluetoothAdapter.ACTION_REQUEST_ENABLE); try {
93 | * startActivity(enableBluetooth); } catch (Exception e) {
94 | * e.printStackTrace(); } // Now implement the onActivityResult()
95 | * and wait for it to // be invoked with ENABLE_BLUETOOTH //
96 | * onActivityResult(ENABLE_BLUETOOTH, result, enableBluetooth);
97 | */return;
98 | }
99 | // Toast.makeText(this, "Bluetooth", Toast.LENGTH_LONG).show();
100 | if (!receiver_registered) {
101 | IntentFilter f1 = new IntentFilter(Bt_iadl.NameFilter);
102 | application.registerReceiver(receiver1, f1);
103 | receiver_registered = true;
104 | }
105 | a2dp.connect2.Bt_iadl.getNames(application);
106 | }
107 |
108 | void createList(int length) {
109 | String[] lstring = new String[length];
110 | for (int j = 0; j < length; j++) {
111 | lstring[j] = temp[j][0] + " - " + temp[j][1];
112 | }
113 |
114 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
115 | builder.setTitle(R.string.BuilderTitle);
116 | builder.setItems(lstring, new DialogInterface.OnClickListener() {
117 | public void onClick(DialogInterface dialog, int item) {
118 | // Use MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE to grant
119 | // access to other applications
120 | SharedPreferences preferences = getSharedPreferences(PREFS, 0);
121 | SharedPreferences.Editor editor = preferences.edit();
122 | String ws = String.valueOf(mAppWidgetId);
123 | editor.putString(ws, temp[item][1]);
124 | dname = temp[item][0];
125 | editor.putString(ws + "_name", dname);
126 | editor.commit();
127 | done();
128 | }
129 | });
130 | builder.show();
131 | }
132 |
133 | BroadcastReceiver receiver1 = new BroadcastReceiver() {
134 |
135 | @Override
136 | public void onReceive(Context context, Intent intent) {
137 | int slength = intent.getIntExtra("slength", 0);
138 | createList(slength);
139 | }
140 |
141 | };
142 |
143 | void done() {
144 | AppWidgetManager appWidgetManager = AppWidgetManager
145 | .getInstance(application);
146 |
147 | Intent intent = new Intent(application, Connector.class);
148 | intent.putExtra("ID", mAppWidgetId);
149 | PendingIntent pendingIntent = PendingIntent.getService(application,
150 | mAppWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
151 |
152 | // Get the layout for the App Widget and attach an on-click listener
153 | // to the button
154 | RemoteViews views = new RemoteViews(application.getPackageName(),
155 | R.layout.widget_initial_layout);
156 | views.setOnClickPendingIntent(R.id.WidgetButton, pendingIntent);
157 |
158 | views.setTextViewText(R.id.WidgetButton, dname);
159 | appWidgetManager.updateAppWidget(mAppWidgetId, views);
160 | Intent resultValue = new Intent();
161 | resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
162 | // resultValue.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
163 | setResult(RESULT_OK, resultValue);
164 |
165 | finish();
166 | }
167 |
168 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/RunUpdate.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Notification;
5 | import android.app.NotificationChannel;
6 | import android.app.NotificationManager;
7 | import android.app.Service;
8 | import android.appwidget.AppWidgetManager;
9 | import android.bluetooth.BluetoothA2dp;
10 | import android.bluetooth.BluetoothAdapter;
11 | import android.bluetooth.BluetoothDevice;
12 | import android.bluetooth.IBluetoothA2dp;
13 | import android.content.ComponentName;
14 | import android.content.Context;
15 | import android.content.Intent;
16 | import android.content.ServiceConnection;
17 | import android.content.SharedPreferences;
18 | import android.content.pm.PackageManager;
19 | import android.os.Build;
20 | import android.os.CountDownTimer;
21 | import android.os.IBinder;
22 | import android.os.RemoteException;
23 | import android.support.v4.app.NotificationCompat;
24 | import android.util.Log;
25 | import android.widget.RemoteViews;
26 | import android.widget.Toast;
27 | import android.support.v4.app.NotificationCompat;
28 | import android.support.v4.app.NotificationManagerCompat;
29 |
30 |
31 | public class RunUpdate extends Service {
32 | @Override
33 | public void onDestroy() {
34 | doUnbindService(this);
35 | super.onDestroy();
36 | }
37 |
38 | public static boolean mIsBound = false;
39 | public static IBluetoothA2dp ibta2 = null;
40 | static String LOG_TAG = "RunUpdate";
41 | private String PREFS = "bluetoothlauncher";
42 | static SharedPreferences preferences;
43 | static Context application;
44 | static RemoteViews views;
45 | private NotificationManager mNotificationManager = null;
46 | private NotificationManagerCompat notificationManagerCompat = null;
47 | private static final String A2DP_FOREGROUND = "a2dp_foreground";
48 | NotificationChannel channel_f;
49 |
50 | public RunUpdate() {
51 |
52 | final BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
53 |
54 | new CountDownTimer(3000, 1000){
55 |
56 | @Override
57 | public void onTick(long l) {
58 |
59 | }
60 |
61 | @Override
62 | public void onFinish() {
63 | Intent intent = new Intent(application, WidgetProvider.class);
64 | intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
65 |
66 | AppWidgetManager awm = AppWidgetManager.getInstance(getApplication());
67 |
68 | int[] ids = awm.getAppWidgetIds(new ComponentName(getApplication(), WidgetProvider.class));
69 |
70 | for(int id: ids){
71 | String bt_mac = preferences.getString(Integer.toString(id), "O");
72 | BluetoothDevice btd = mBTA.getRemoteDevice(bt_mac);
73 | if (bt_mac.length() == 17 && isDeviceConnected(bt_mac)) {
74 | views.setInt(R.id.WidgetButton, "setBackgroundResource", R.drawable.icon);
75 |
76 | } else {
77 | views.setInt(R.id.WidgetButton, "setBackgroundResource", R.drawable.icon2);
78 |
79 | }
80 | awm.updateAppWidget(id,views);
81 | }
82 |
83 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
84 | sendBroadcast(intent);
85 |
86 | stopSelf();
87 | }
88 | }.start();
89 | }
90 |
91 | @Override
92 | public int onStartCommand(Intent intent, int flags, int startId) {
93 | application = getApplicationContext();
94 | preferences = getApplicationContext().getSharedPreferences(PREFS, 0);
95 | views = new RemoteViews(this.getPackageName(),
96 | R.layout.widget_initial_layout);
97 | if(!mIsBound) getIBluetoothA2dp(this);
98 | return super.onStartCommand(intent, flags, startId);
99 | }
100 |
101 | @Override
102 | public void onCreate() {
103 | application = getApplicationContext();
104 | preferences = getApplicationContext().getSharedPreferences(PREFS, 0);
105 | views = new RemoteViews(this.getPackageName(),
106 | R.layout.widget_initial_layout);
107 | if(!mIsBound) getIBluetoothA2dp(this);
108 | notificationManagerCompat = NotificationManagerCompat.from(application);
109 | super.onCreate();
110 | }
111 |
112 | @Override
113 | public IBinder onBind(Intent intent) {
114 | // TODO: Return the communication channel to the service.
115 | throw new UnsupportedOperationException("Not yet implemented");
116 | }
117 |
118 | static void getIBluetoothA2dp(Context context) {
119 |
120 | Intent i = new Intent(IBluetoothA2dp.class.getName());
121 |
122 | String filter;
123 | filter = context.getPackageManager().resolveService(i, PackageManager.GET_RESOLVED_FILTER).serviceInfo.packageName;
124 | i.setPackage(filter);
125 |
126 | if (context.bindService(i, mConnection, Context.BIND_AUTO_CREATE)) {
127 | //Log.i(LOG_TAG, "mConnection service bound");
128 | //Toast.makeText(context, "started service connection", Toast.LENGTH_SHORT).show();
129 | } else {
130 | Toast.makeText(context, "Bluetooth start service connection failed", Toast.LENGTH_SHORT).show();
131 | //Log.e(LOG_TAG, "Could not bind to Bluetooth A2DP Service");
132 | }
133 |
134 | }
135 |
136 |
137 | public static ServiceConnection mConnection = new ServiceConnection() {
138 |
139 | @Override
140 | public void onServiceConnected(ComponentName name, IBinder service) {
141 |
142 | mIsBound = true;
143 | ibta2 = IBluetoothA2dp.Stub.asInterface(service);
144 | //sendIntent();
145 | }
146 |
147 | @Override
148 | public void onServiceDisconnected(ComponentName name) {
149 | mIsBound = false;
150 |
151 | }
152 |
153 | };
154 |
155 | public static boolean isDeviceConnected(String btd) {
156 | BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
157 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(btd);
158 |
159 |
160 | int sinkState = 0;
161 | if (device != null) {
162 | try {
163 | sinkState = ibta2.getConnectionState(device);
164 | } catch (RemoteException e) {
165 | e.printStackTrace();
166 | }
167 | }
168 | Boolean connected = sinkState == BluetoothA2dp.STATE_CONNECTED || sinkState == BluetoothA2dp.STATE_CONNECTING;
169 |
170 | Boolean result = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
171 | && mBluetoothAdapter.getProfileConnectionState(BluetoothA2dp.A2DP) == BluetoothA2dp.STATE_CONNECTED && connected ;
172 | Log.i(LOG_TAG, "Mac connected " + btd + " - " + result);
173 | return result;
174 | }
175 |
176 | public static void doUnbindService(Context context) {
177 | if (mIsBound) {
178 | // Detach our existing connection.
179 | try {
180 | context.unbindService(mConnection);
181 | } catch (Exception e) {
182 | // TODO Auto-generated catch block
183 | e.printStackTrace();
184 | }
185 |
186 | }
187 |
188 |
189 | }
190 |
191 | // This will only be needed if I ever start this as a foreground service.
192 |
193 | private void updatenot() {
194 | if (channel_f == null) createNotificationChannel();
195 | if (mNotificationManager != null) {
196 | mNotificationManager.cancelAll();
197 | } else {
198 | createNotificationChannel();
199 | }
200 | if (notificationManagerCompat != null) {
201 | notificationManagerCompat.cancelAll();
202 | } else {
203 | createNotificationChannel();
204 | }
205 |
206 | String temp = "";
207 | Notification not = null;
208 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
209 | not = new NotificationCompat.Builder(application, A2DP_FOREGROUND)
210 | .setContentTitle(
211 | getResources().getString(R.string.app_name))
212 |
213 | .setSmallIcon(R.drawable.icon)
214 | .setCategory(Notification.CATEGORY_SERVICE)
215 | .setContentText(temp)
216 | .setChannelId(A2DP_FOREGROUND).build();
217 | notificationManagerCompat.notify(1, not);
218 | //Toast.makeText(application, "Test on " + car + " " +not.getChannelId(), Toast.LENGTH_LONG).show();
219 | } else {
220 | not = new NotificationCompat.Builder(application, LAUNCHER_APPS_SERVICE)
221 | .setContentTitle(
222 | getResources().getString(R.string.app_name))
223 | //.setContentIntent(contentIntent)
224 | .setSmallIcon(R.drawable.icon)
225 | .setContentText(temp)
226 | .setPriority(Notification.PRIORITY_LOW)
227 | .build();
228 | notificationManagerCompat.notify(1, not);
229 | }
230 | this.startForeground(1, not);
231 | }
232 |
233 | @TargetApi(Build.VERSION_CODES.M)
234 | private void createNotificationChannel() {
235 | mNotificationManager = getSystemService(NotificationManager.class);
236 | //notificationManagerCompat = NotificationManagerCompat.from(application);
237 |
238 | // Create the NotificationChannel, but only on API 26+ because
239 | // the NotificationChannel class is new and not in the support library
240 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
241 |
242 | // set up foreground notification channel
243 | CharSequence name2 = getString(R.string.foreground_channel_name);
244 | String description2 = getString(R.string.foreground_channel_description);
245 | int importance2 = NotificationManager.IMPORTANCE_LOW;
246 | channel_f = new NotificationChannel(A2DP_FOREGROUND, name2, importance2);
247 | channel_f.setDescription(description2);
248 | // Register the channel with the system; you can't change the importance
249 | // or other notification behaviors after this
250 | mNotificationManager.createNotificationChannel(channel_f);
251 |
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/app/src/main/java/a2dp/connect2/Connector.java:
--------------------------------------------------------------------------------
1 | package a2dp.connect2;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 | import java.lang.reflect.Method;
5 | import java.util.Set;
6 |
7 | import android.annotation.TargetApi;
8 | import android.app.Notification;
9 | import android.app.NotificationChannel;
10 | import android.app.NotificationManager;
11 | import android.app.Service;
12 | import android.appwidget.AppWidgetManager;
13 | import android.bluetooth.BluetoothA2dp;
14 | import android.bluetooth.BluetoothAdapter;
15 | import android.bluetooth.BluetoothDevice;
16 | import android.bluetooth.IBluetooth;
17 | import android.bluetooth.IBluetoothA2dp;
18 | import android.content.BroadcastReceiver;
19 | import android.content.ComponentName;
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.content.IntentFilter;
23 | import android.content.ServiceConnection;
24 | import android.content.SharedPreferences;
25 | import android.content.pm.ApplicationInfo;
26 | import android.content.pm.PackageManager;
27 | import android.os.AsyncTask;
28 | import android.os.Build;
29 | import android.os.Bundle;
30 | import android.os.CountDownTimer;
31 | import android.os.IBinder;
32 | import android.os.RemoteException;
33 | import android.support.annotation.RequiresApi;
34 | import android.support.v4.app.NotificationCompat;
35 | import android.support.v4.app.NotificationManagerCompat;
36 | import android.util.Log;
37 | import android.widget.RemoteViews;
38 | import android.widget.Toast;
39 |
40 | import static a2dp.connect2.Bt_iadl.c1;
41 | import static a2dp.connect2.Bt_iadl.filter_1_string;
42 | import static a2dp.connect2.Bt_iadl.ibt2;
43 | import static a2dp.connect2.Bt_iadl.ibta2;
44 | import static a2dp.connect2.Bt_iadl.mIsBound;
45 |
46 |
47 | public class Connector extends Service {
48 |
49 | public static Context application;
50 | private static String DeviceToConnect;
51 | private static final String A2DP_FOREGROUND = "a2dp_foreground";
52 | NotificationChannel channel_f;
53 | static final int ENABLE_BLUETOOTH = 1;
54 | private String PREFS = "bluetoothlauncher";
55 | private static String LOG_TAG = "A2DP_Connect";
56 | private BluetoothDevice device = null;
57 | private String dname;
58 | private String bt_mac;
59 | boolean serviceRegistered = false;
60 | boolean receiverRegistered = false;
61 | private NotificationManager mNotificationManager = null;
62 | private NotificationManagerCompat notificationManagerCompat = null;
63 |
64 | int w_id;
65 |
66 | @Override
67 | public void onDestroy() {
68 | //this.unregisterReceiver(receiver);
69 |
70 | Log.i(LOG_TAG, "OnDestroy called");
71 | done();
72 | super.onDestroy();
73 | }
74 |
75 | @Override
76 | protected void finalize() throws Throwable {
77 |
78 | done();
79 | super.finalize();
80 | }
81 |
82 | @Override
83 | public IBinder onBind(Intent intent) {
84 | return null;
85 | }
86 |
87 | @Override
88 | public int onStartCommand(Intent intent, int flags, int startId) {
89 | Bundle extras = intent.getExtras();
90 |
91 | application = getApplicationContext();
92 |
93 | if (extras != null) {
94 | w_id = extras.getInt("ID", 0);
95 |
96 | Log.i(LOG_TAG, "Starting " + w_id);
97 | } else {
98 | Toast.makeText(application, "Oops", Toast.LENGTH_LONG).show();
99 | done();
100 | }
101 |
102 | SharedPreferences preferences = getSharedPreferences(PREFS, 0);
103 | bt_mac = preferences.getString(String.valueOf(w_id), "");
104 | dname = preferences.getString(w_id + "_name", "oops");
105 | DeviceToConnect = bt_mac;
106 | Log.i(LOG_TAG, "Device MAC = " + bt_mac);
107 |
108 | if (bt_mac != null)
109 | if (bt_mac.length() == 17) {
110 |
111 | BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
112 |
113 | if (!bta.isEnabled()) {
114 | Intent btIntent = new Intent(
115 | BluetoothAdapter.ACTION_REQUEST_ENABLE);
116 | btIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
117 | application.startActivity(btIntent);
118 | Log.i(LOG_TAG, "Bluetooth was not enabled, starting...");
119 | return START_REDELIVER_INTENT;
120 | }
121 |
122 | BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
123 | if (mBTA == null || !mBTA.isEnabled()) {
124 | Log.i(LOG_TAG, "Bluetooth issue");
125 | return START_REDELIVER_INTENT;
126 | }
127 |
128 | Set pairedDevices = bta.getBondedDevices();
129 | for (BluetoothDevice dev : pairedDevices) {
130 | if (dev.getAddress().equalsIgnoreCase(bt_mac))
131 | device = dev;
132 | }
133 | if (device == null) {
134 | Log.i(LOG_TAG, "Device was NULL");
135 | return START_REDELIVER_INTENT;
136 | }
137 |
138 | getIBluetoothA2dp(application);
139 |
140 | if (!receiverRegistered) {
141 | String filter_1_string = "a2dp.connect2.Connector.INTERFACE";
142 | IntentFilter filter1 = new IntentFilter(filter_1_string);
143 | application.registerReceiver(receiver, filter1);
144 | receiverRegistered = true;
145 | }
146 |
147 | sendIntent();
148 | updatenot();
149 | //connectBluetoothA2dp(bt_mac);
150 |
151 | } else {
152 | Toast.makeText(application,
153 | getString(R.string.InvalidDevice) + " " + bt_mac,
154 | Toast.LENGTH_LONG).show();
155 | Log.i(LOG_TAG, "Invalid device = " + bt_mac);
156 | done();
157 | }
158 |
159 | else {
160 | Log.e(LOG_TAG, "Device to connect was NULL");
161 | Toast.makeText(application, getString(R.string.NullDevice),
162 | Toast.LENGTH_LONG).show();
163 | done();
164 | }
165 | return START_NOT_STICKY;
166 | // super.onStart(intent, startId);
167 | }
168 |
169 | private static void sendIntent() {
170 | Intent intent = new Intent();
171 | intent.setAction(filter_1_string);
172 | application.sendBroadcast(intent);
173 | }
174 |
175 |
176 | private final BroadcastReceiver receiver = new BroadcastReceiver() {
177 |
178 | @Override
179 | public void onReceive(Context arg0, Intent arg1) {
180 | IBluetoothA2dp ibta = ibta2;
181 |
182 | //Log.i(LOG_TAG, "Received broadcast ");
183 |
184 | try {
185 | if (ibta != null && ibta.getConnectionState(device) == 0) {
186 | Toast.makeText(application,
187 | getString(R.string.Connecting) + " " + dname,
188 | Toast.LENGTH_LONG).show();
189 | } else {
190 | Toast.makeText(application,
191 | getString(R.string.Disconnecting) + " " + dname,
192 | Toast.LENGTH_LONG).show();
193 | }
194 | } catch (RemoteException e) {
195 | // TODO Auto-generated catch block
196 | e.printStackTrace();
197 | }
198 | connectBluetoothA2dp(bt_mac);
199 | }
200 |
201 | };
202 |
203 | /**
204 | * @see android.app.Activity#onCreate(Bundle)
205 | */
206 |
207 | public void onCreate() {
208 | // super.onCreate();
209 | application = getApplication();
210 | //mNotificationManager = getSystemService(NotificationManager.class);
211 | notificationManagerCompat = NotificationManagerCompat.from(application);
212 |
213 | if (!receiverRegistered) {
214 | String filter_1_string = "a2dp.connect2.Connector.INTERFACE";
215 | IntentFilter filter1 = new IntentFilter(filter_1_string);
216 | application.registerReceiver(receiver, filter1);
217 | receiverRegistered = true;
218 | }
219 | getIBluetoothA2dp(application);
220 | serviceRegistered = true;
221 | }
222 |
223 | private void connectBluetoothA2dp(String device) {
224 | Log.i(LOG_TAG, "Device = " + device);
225 | new ConnectBt().execute(device);
226 | }
227 |
228 |
229 | public static ServiceConnection mConnection = new ServiceConnection() {
230 |
231 | @Override
232 | public void onServiceConnected(ComponentName name, IBinder service) {
233 |
234 | mIsBound = true;
235 | ibta2 = IBluetoothA2dp.Stub.asInterface(service);
236 | BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
237 |
238 | Set pairedDevices = mBTA.getBondedDevices();
239 | BluetoothDevice device = null;
240 | for (BluetoothDevice dev : pairedDevices) {
241 | if (dev.getAddress().equalsIgnoreCase(DeviceToConnect))
242 | device = dev;
243 | }
244 | if (device != null)
245 | try {
246 | Log.i(LOG_TAG, "Service connecting " + device);
247 |
248 | } catch (Exception e) {
249 | e.printStackTrace();
250 | Log.e(LOG_TAG, "Error connecting Bluetooth device " + e.getLocalizedMessage());
251 | }
252 | }
253 |
254 | @Override
255 | public void onServiceDisconnected(ComponentName name) {
256 | mIsBound = false;
257 | doUnbind();
258 | }
259 | };
260 |
261 | static void doUnbind() {
262 | if (mIsBound) {
263 | try {
264 | application.unbindService(mConnection);
265 | } catch (Exception e) {
266 | e.printStackTrace();
267 | }
268 | }
269 | }
270 |
271 | public void getIBluetoothA2dp(Context context) {
272 |
273 | Intent i = new Intent(IBluetoothA2dp.class.getName());
274 |
275 | String filter;
276 | filter = getPackageManager().resolveService(i, PackageManager.GET_RESOLVED_FILTER).serviceInfo.packageName;
277 | i.setPackage(filter);
278 |
279 | if (context.bindService(i, mConnection, Context.BIND_AUTO_CREATE)) {
280 | Log.i(LOG_TAG, "mConnection service bound " + context.getPackageCodePath());
281 | //Toast.makeText(context, "started service connection", Toast.LENGTH_SHORT).show();
282 | } else {
283 | Toast.makeText(context, "Bluetooth start service connection failed", Toast.LENGTH_SHORT).show();
284 | Log.e(LOG_TAG, "Could not bind to Bluetooth A2DP Service");
285 | }
286 |
287 | }
288 |
289 | private class ConnectBt extends AsyncTask {
290 |
291 | /*
292 | * (non-Javadoc)
293 | *
294 | * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
295 | */
296 |
297 | String btd;
298 |
299 | @Override
300 | protected void onPostExecute(Boolean result) {
301 | Intent intent = new Intent(application, RunUpdate.class);
302 | intent.putExtra("BT", btd);
303 | application.startService(intent);
304 |
305 | done();
306 | super.onPostExecute(result);
307 | }
308 |
309 | BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter();
310 |
311 | protected void onPreExecute() {
312 | //Log.i(LOG_TAG, "Running background task with ");
313 | }
314 |
315 | @Override
316 | protected Boolean doInBackground(String... arg0) {
317 |
318 | BluetoothAdapter mBTA = BluetoothAdapter.getDefaultAdapter();
319 | if (mBTA == null || !mBTA.isEnabled())
320 | return false;
321 |
322 | Set pairedDevices = bta.getBondedDevices();
323 | BluetoothDevice device = null;
324 | for (BluetoothDevice dev : pairedDevices) {
325 | if (dev.getAddress().equalsIgnoreCase(arg0[0]))
326 | device = dev;
327 | }
328 | if (device == null)
329 | return false;
330 |
331 | btd = device.getAddress();
332 | /*
333 | * mBTA.cancelDiscovery(); mBTA.startDiscovery();
334 | */
335 | IBluetoothA2dp ibta = ibta2;
336 |
337 | try {
338 |
339 | if (ibta != null && ibta.getConnectionState(device) == 0) {
340 | ibta.connect(device);
341 | Log.i(LOG_TAG, "Connecting: " + device.getName());
342 |
343 | } else {
344 | ibta.disconnect(device);
345 | Log.i(LOG_TAG, "Disconnecting: " + device.getName());
346 | }
347 |
348 | } catch (Exception e) {
349 | Log.e(LOG_TAG, "Error " + e.getMessage());
350 | }
351 |
352 |
353 | return true;
354 | }
355 |
356 | }
357 |
358 |
359 | private void done() {
360 | Log.i(LOG_TAG, "Service stopping");
361 | if (receiverRegistered) {
362 | try {
363 | application.unregisterReceiver(receiver);
364 | receiverRegistered = false;
365 | } catch (Exception e) {
366 | e.printStackTrace();
367 | }
368 | }
369 | if (serviceRegistered) {
370 | try {
371 | //doUnbindService(application);
372 | } catch (Exception e) {
373 | e.printStackTrace();
374 | }
375 | }
376 |
377 | mNotificationManager.cancel(1);
378 | mNotificationManager.cancelAll();
379 | notificationManagerCompat.cancelAll();
380 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
381 | this.stopForeground(Service.STOP_FOREGROUND_REMOVE);
382 | }
383 | this.stopForeground(true);
384 | this.stopSelf();
385 |
386 | }
387 |
388 | private void updatenot() {
389 | if (channel_f == null) createNotificationChannel();
390 | if (mNotificationManager != null) {
391 | mNotificationManager.cancelAll();
392 | } else {
393 | createNotificationChannel();
394 | }
395 | if (notificationManagerCompat != null) {
396 | notificationManagerCompat.cancelAll();
397 | } else {
398 | createNotificationChannel();
399 | }
400 |
401 | String temp = "";
402 | Notification not = null;
403 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
404 | not = new NotificationCompat.Builder(application, A2DP_FOREGROUND)
405 | .setContentTitle(
406 | getResources().getString(R.string.app_name))
407 |
408 | .setSmallIcon(R.drawable.icon)
409 | .setCategory(Notification.CATEGORY_SERVICE)
410 | .setContentText(temp)
411 | .setChannelId(A2DP_FOREGROUND).build();
412 | notificationManagerCompat.notify(1, not);
413 | //Toast.makeText(application, "Test on " + car + " " +not.getChannelId(), Toast.LENGTH_LONG).show();
414 | } else {
415 | not = new NotificationCompat.Builder(application, LAUNCHER_APPS_SERVICE)
416 | .setContentTitle(
417 | getResources().getString(R.string.app_name))
418 | //.setContentIntent(contentIntent)
419 | .setSmallIcon(R.drawable.icon)
420 | .setContentText(temp)
421 | .setPriority(Notification.PRIORITY_LOW)
422 | .build();
423 | notificationManagerCompat.notify(1, not);
424 | }
425 | this.startForeground(1, not);
426 | }
427 |
428 | @TargetApi(Build.VERSION_CODES.M)
429 | private void createNotificationChannel() {
430 | mNotificationManager = getSystemService(NotificationManager.class);
431 | //notificationManagerCompat = NotificationManagerCompat.from(application);
432 |
433 | // Create the NotificationChannel, but only on API 26+ because
434 | // the NotificationChannel class is new and not in the support library
435 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
436 |
437 | // set up foreground notification channel
438 | CharSequence name2 = getString(R.string.foreground_channel_name);
439 | String description2 = getString(R.string.foreground_channel_description);
440 | int importance2 = NotificationManager.IMPORTANCE_LOW;
441 | channel_f = new NotificationChannel(A2DP_FOREGROUND, name2, importance2);
442 | channel_f.setDescription(description2);
443 | // Register the channel with the system; you can't change the importance
444 | // or other notification behaviors after this
445 | mNotificationManager.createNotificationChannel(channel_f);
446 |
447 | }
448 | }
449 | }
450 |
--------------------------------------------------------------------------------