diff --git a/README.md b/README.md index 9997594..358d03e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# fay-android -app会常驻手机后台,你可以随时随地保持与Fay数字人的沟通。 +# android 连接器 +android 连接器 app会常驻手机后台,你可以随时随地保持与Fay数字人(https://github.com/TheRamU/Fay)的沟通。 + +![image-20231225180134156](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20231225180134156.png) + + + + + +**使用说明** + +1、工程使用android studio 开发,可以直接下载apk运行。 + +2、若需要让app在公网环境保持与Fay的互通,在Fay中配置ngrok.cc 的id,并在服务器地址栏填上分配域名及端口(无须填写协议)。 + +**更新说明** + +2023.12.25: + +1、增加重连机制; + +2、优化麦克风及蓝牙sco管理机制; + +3、增加麦克风开关及服务器地址管理。 \ No newline at end of file diff --git a/fayConnectorDemo/.gitignore b/fayConnectorDemo/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/fayConnectorDemo/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/fayConnectorDemo/.idea/.gitignore b/fayConnectorDemo/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/fayConnectorDemo/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/fayConnectorDemo/.idea/compiler.xml b/fayConnectorDemo/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/fayConnectorDemo/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/.idea/gradle.xml b/fayConnectorDemo/.idea/gradle.xml new file mode 100644 index 0000000..a2d7c21 --- /dev/null +++ b/fayConnectorDemo/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/.idea/misc.xml b/fayConnectorDemo/.idea/misc.xml new file mode 100644 index 0000000..1166903 --- /dev/null +++ b/fayConnectorDemo/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/.gitignore b/fayConnectorDemo/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/fayConnectorDemo/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/fayConnectorDemo/app/build.gradle b/fayConnectorDemo/app/build.gradle new file mode 100644 index 0000000..5715e7d --- /dev/null +++ b/fayConnectorDemo/app/build.gradle @@ -0,0 +1,39 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.yaheen.fayconnectordemo" + minSdk 29 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + +} \ No newline at end of file diff --git a/fayConnectorDemo/app/proguard-rules.pro b/fayConnectorDemo/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/fayConnectorDemo/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/fayConnectorDemo/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java b/fayConnectorDemo/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java new file mode 100644 index 0000000..3035a79 --- /dev/null +++ b/fayConnectorDemo/app/src/androidTest/java/com/yaheen/fayconnectordemo/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.yaheen.fayconnectordemo; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.yaheen.fayconnectordemo", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/AndroidManifest.xml b/fayConnectorDemo/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..57506be --- /dev/null +++ b/fayConnectorDemo/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java new file mode 100644 index 0000000..960fc9c --- /dev/null +++ b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/FayConnectorService.java @@ -0,0 +1,428 @@ +package com.yaheen.fayconnectordemo; + +import android.Manifest; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.graphics.BitmapFactory; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaPlayer; +import android.media.MediaRecorder; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; + +import com.google.android.material.snackbar.Snackbar; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Arrays; +import java.util.Date; + +public class FayConnectorService extends Service { + private AudioRecord record; + private int recordBufsize = 0; + private Socket socket = null; + private InputStream in = null; + private OutputStream out = null; + public static boolean running = false; + private File cacheDir = null; + private String channelId = null; + private PendingIntent pendingIntent = null; + private NotificationManagerCompat notificationManager = null; + private long totalrece = 0; + private long totalsend = 0; + private AudioManager mAudioManager = null; + private Thread sendThread = null; + private Thread receThread = null; + private boolean isPlay = false; + private boolean isMic = false; + private boolean isRecordStarted = false; + BroadcastReceiver scoReceiver; + + + //创建通知 + private String createNotificationChannel(String channelID, String channelNAME, int level) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationChannel channel = new NotificationChannel(channelID, channelNAME, level); + manager.createNotificationChannel(channel); + return channelID; + } else { + return null; + } + } + + // 定义广播的动作字符串 + public static final String ACTION_CONTROL_MIC = "com.yaheen.fayconnectordemo.ACTION_CONTROL_MIC"; + + private final BroadcastReceiver micControlReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + boolean micEnabled = intent.getBooleanExtra("mic", false); + if (micEnabled) { + isMic = true; + } else { + isMic = false; + } + } + }; + private void startMicrophone() { + //开启sco + mAudioManager.startBluetoothSco(); + mAudioManager.setMode(mAudioManager.MODE_IN_CALL); + mAudioManager.setBluetoothScoOn(true); + //开始录音 + record.startRecording(); + isRecordStarted = true; + Log.d("fay", "麦克风启动成功"); + } + + private void stopMicrophone() { + //关闭sco + mAudioManager.stopBluetoothSco(); + mAudioManager.setBluetoothScoOn(false); + mAudioManager.setMode(mAudioManager.MODE_NORMAL); + //停止录音 + record.stop(); + isRecordStarted = false; + Log.d("fay", "麦克风关闭成功"); + } + + + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, START_FLAG_REDELIVERY, startId); + isMic = intent.getBooleanExtra("mic", false); + return Service.START_STICKY; + + } + + @Override + public void onCreate() { + super.onCreate(); + Log.d("fay", "服务启动"); + + running = true; + this.cacheDir = getApplicationContext().getFilesDir();//getCacheDir(); + + // 注册广播接收器 + IntentFilter filter = new IntentFilter(ACTION_CONTROL_MIC); + registerReceiver(micControlReceiver, filter); + + //蓝牙sco状态监听 + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); + scoReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1); + if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) { + Log.d("fay", "蓝牙sco连接成功"); + } + if (AudioManager.SCO_AUDIO_STATE_DISCONNECTED == state) { + Log.d("fay", "蓝牙sco关闭"); + } + } + }; + this.registerReceiver(scoReceiver, intentFilter); + + //连接socket + String serverAddress = KVUtils.readData(getApplicationContext(), "ServerAddress"); + if (serverAddress == null || serverAddress.split(":").length != 2) { + return; + } + + //启动发送线程 + sendThread = new Thread(new Runnable() { + @Override + public void run() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ContextCompat.checkSelfPermission(FayConnectorService.this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + if (record == null) { + recordBufsize = AudioRecord + .getMinBufferSize(16000, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT); + record = new AudioRecord(MediaRecorder.AudioSource.MIC, + 16000, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT, + recordBufsize); + + } + + byte[] data = new byte[1024]; + Log.d("fay", "开始传输音频"); + while (running) { + try { + Thread.sleep(10); + }catch (Exception e){} + if (socket == null || socket.isClosed()){ + try { + Thread.sleep(10000); + }catch (Exception ee){ + } + reconnectSocket(); + } + if (isPlay){ + continue; + } + if (!isMic){ + if (isRecordStarted) { + stopMicrophone(); + } + continue; + }else { + if (!isRecordStarted) { //isPlay == true时在上面已经continue + startMicrophone(); + } + } + int size = record.read(data, 0, 1024); + if (size > 0) { + try { + out.write(data); + }catch (Exception e){ + Log.d("fay", "socket断开10秒后重连"); + socket = null; + continue; + } + totalsend += data.length / 1024; + } else {//麦克风被占用了,等待10秒重新录取 + stopMicrophone(); + try { + Thread.sleep(10000); + } catch (Exception e) { + } + } + } + running = false; + record.stop(); + record = null; + ((AudioManager) getSystemService(Context.AUDIO_SERVICE)).stopBluetoothSco(); + try { + socket.close(); + } catch (Exception e) { + } + socket = null; + Log.d("fay", "send线程结束"); + } + } + + } + }); + sendThread.start(); + + //启动接收线程 + receThread = new Thread(new Runnable() { + @Override + public void run() { + while (running) { + try { + if (socket == null || socket.isClosed()) { + try { + Thread.sleep(1000); + } catch (Exception e) { + } + continue; + } + byte[] data = new byte[9]; + byte[] wavhead = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};//文件传输开始标记 + in.read(data); + if (Arrays.equals(wavhead, data)) { + Log.d("fay", "开始接收音频文件"); + String filedata = ""; + data = new byte[1024]; + int len = 0; + while ((len = in.read(data)) != -1) { + byte[] temp = new byte[len]; + System.arraycopy(data, 0, temp, 0, len); + filedata += MainActivity.bytesToHexString(temp); + int index = filedata.indexOf("080706050403020100"); + if (filedata.length() > 9 && index > 0) {//wav文件结束标记 + filedata = filedata.substring(0, index).replaceAll("F0F1F2F3F4F5F6F7F8", ""); + File wavFile = new File(cacheDir, String.format("sample-%s.mp3", new Date().getTime() + "")); + wavFile.createNewFile(); + FileOutputStream fos = new FileOutputStream(wavFile); + fos.write(MainActivity.decodeHexBytes(filedata.toCharArray())); + fos.close(); + totalrece += filedata.length() / 2 / 1024; + Log.d("fay", "mp3文件接收完成:" + wavFile.getAbsolutePath() + "," + filedata.length() / 2); + try { + MediaPlayer player = new MediaPlayer(); + player.setDataSource(wavFile.getAbsolutePath()); + player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + isPlay = true; + Log.d("fay", "开始播放"); + if (isRecordStarted) { + stopMicrophone(); + } + try { + Thread.sleep(100); + } catch (Exception e) { + + } + isPlay = true; + mp.start(); + } + }); + player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + + @Override + public void onCompletion(MediaPlayer mp) { + Log.d("fay", "播放完成"); + mp.release(); + if (isRecordStarted) { + startMicrophone(); + } + isPlay = false; + } + + }); + player.setVolume(1, 1); + player.setLooping(false); + player.prepareAsync(); + + } catch (IOException e) { + Log.e("fay", e.toString()); + } + break; + } + + } + Thread.sleep(300); + } + Thread.sleep(300); + } catch (Exception e) { + e.printStackTrace(); + } + } + Log.d("fay", "rece线程结束"); + } + }); + receThread.start(); + + //通知栏 + new Thread(new Runnable() { + @Override + public void run() { + try{ + while (running) { + Thread.sleep(3000); + String statusStr = socket == null ? "正在连接" : "已经连接"; + if (totalsend + totalrece > 2048){ + inotify("fay connector demo", statusStr + "fay控制器,累计接收/发送:" + String.format("%.2f", (double)totalrece / 1024) + "/" + String.format("%.2f", (double)totalsend / 1024) + "MB"); + } else { + inotify("fay connector demo", statusStr + "fay控制器,累计接收/发送:" + totalrece + "/" + totalsend + "KB"); + } + } + inotify("fay connector demo", "已经断开fay控制器"); + }catch (Exception e){ + Log.e("fay", e.toString()); + }finally { + FayConnectorService.this.stopSelf(); + } + } + }).start(); + + + } + + private void inotify(String title, String content){ + Intent intent = new Intent(this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + if (pendingIntent == null){ + pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); + } + if (channelId == null){ + channelId = createNotificationChannel("my_channel_ID", "my_channel_NAME", NotificationManager.IMPORTANCE_HIGH); + } + if (notificationManager == null){ + notificationManager = NotificationManagerCompat.from(this); + } + NotificationCompat.Builder notification2 = new NotificationCompat.Builder(FayConnectorService.this, channelId) + .setContentTitle(title) + .setContentText(content) + .setContentIntent(pendingIntent) + .setSmallIcon(R.drawable.icon) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setAutoCancel(true); + //notificationManager.notify(100, notification2.build()); + startForeground(100, notification2.build()); + } + + + @Override + public void onDestroy() { + Log.d("fay", "服务关闭"); + super.onDestroy(); + running = false; + if (isRecordStarted){ + stopMicrophone(); + } + if (socket != null && !socket.isClosed()) { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + stopForeground(true); + unregisterReceiver(micControlReceiver); + unregisterReceiver(scoReceiver); + } + + private void reconnectSocket() { + if (socket != null && !socket.isClosed()) { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + String serverAddress = KVUtils.readData(getApplicationContext(), "ServerAddress"); + if (serverAddress == null || serverAddress.split(":").length != 2) { + return; + } + + try { + socket = new Socket(serverAddress.split(":")[0], Integer.parseInt(serverAddress.split(":")[1])); + in = socket.getInputStream(); + out = socket.getOutputStream(); + Log.d("fay", "重新连接 fay 控制器成功"); + } catch (IOException e) { + Log.e("fay", "重新连接 fay 控制器失败", e); + } + } + +} diff --git a/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/KVUtils.java b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/KVUtils.java new file mode 100644 index 0000000..3b5630f --- /dev/null +++ b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/KVUtils.java @@ -0,0 +1,96 @@ +package com.yaheen.fayconnectordemo; + +import android.content.Context; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class KVUtils { + + private static final String FILE_NAME = "kv_data.txt"; + + // 写入数据 + public static void writeData(Context context, String key, String value) { + File file = new File(context.getFilesDir(), FILE_NAME); + Map dataMap = new HashMap<>(); + + // 先读取现有的内容到 Map + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("=", 2); + if (parts.length >= 2) { + dataMap.put(parts[0], parts[1]); + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // 更新或添加新的键值对 + dataMap.put(key, value); + + // 将更新后的数据写回文件 + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file, false)); // false 以覆盖方式写入 + for (Map.Entry entry : dataMap.entrySet()) { + writer.write(entry.getKey() + "=" + entry.getValue()); + writer.newLine(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + + // 读取数据 + public static String readData(Context context, String key) { + File file = new File(context.getFilesDir(), FILE_NAME); + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("=", 2); + if (parts.length >= 2 && parts[0].equals(key)) { + return parts[1]; + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } +} diff --git a/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java new file mode 100644 index 0000000..41ef873 --- /dev/null +++ b/fayConnectorDemo/app/src/main/java/com/yaheen/fayconnectordemo/MainActivity.java @@ -0,0 +1,178 @@ +package com.yaheen.fayconnectordemo; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import android.Manifest; +import android.app.ActivityManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaPlayer; +import android.media.MediaRecorder; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Switch; +import android.widget.TextView; + +import com.google.android.material.snackbar.Snackbar; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class MainActivity extends AppCompatActivity { + private TextView tv = null; + private EditText serverAddress = null; + private Switch microphoneSwitch = null; + private boolean running = false; + private Intent serviceIntent = null; + + @Override + protected void onResume() { + super.onResume(); + String serverAddressStr = KVUtils.readData(getApplicationContext(), "ServerAddress"); + serverAddress.setText(serverAddressStr == null ? "192.168.1.101:10001" : serverAddressStr); + microphoneSwitch.setChecked(Boolean.parseBoolean(KVUtils.readData(getApplicationContext(), "IsMic"))); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + tv = this.findViewById(R.id.tv); + serverAddress = this.findViewById(R.id.server_address); + microphoneSwitch = this.findViewById(R.id.microphone_switch); + + String serverAddressStr = KVUtils.readData(getApplicationContext(), "ServerAddress"); + serverAddress.setText(serverAddressStr == null ? "192.168.1.101:10001" : serverAddressStr); + microphoneSwitch.setChecked(Boolean.parseBoolean(KVUtils.readData(getApplicationContext(), "IsMic"))); + + serviceIntent = new Intent(this, FayConnectorService.class); + + //按钮点击 + tv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.d("fay","onclick"); + String address = serverAddress.getText().toString(); + if (address == null) { + return; + } + + //记录服务器信息 + KVUtils.writeData(getApplicationContext(), "ServerAddress", address); + + running = FayConnectorService.running;//isServiceRunning();//同步service的运行状态,不好使! + if (!running){//运行 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//开启 + if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.RECORD_AUDIO)) { + Log.d("fay", "用户彻底拒绝了权限"); + return; + } else { + // 用户未彻底拒绝授予权限 + ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, 1); + } + } + + if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + Log.d("fay","权限ok"); + + Snackbar.make(view, "正在连接fay控制器", Snackbar.LENGTH_SHORT) + .setAction("Action", null).show(); + serviceIntent.putExtra("mic", microphoneSwitch.isChecked()); + startForegroundService(serviceIntent); + running = true; + } + } + } else{//关闭 + stopService(serviceIntent); + Snackbar.make(view, "已经断开fay控制器", Snackbar.LENGTH_SHORT) + .setAction("Action", null).show(); + running = false; + } + + } + }); + + // 开关状态更改事件 + microphoneSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + Intent intent = new Intent(FayConnectorService.ACTION_CONTROL_MIC); + intent.putExtra("mic", isChecked); + sendBroadcast(intent);//通知service麦克风状态改变 + KVUtils.writeData(getApplicationContext(), "IsMic", isChecked + "");//记录状态 + + } + }); + } + + + public static String bytesToHexString(byte[] data){ + String result=""; + for (int i = 0; i < data.length; i++) { + result+=Integer.toHexString((data[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3); + } + return result; + } + + + public static byte[] decodeHexBytes(char[] data) { + int len = data.length; + if ((len & 0x01) != 0) { + throw new RuntimeException("未知的字符"); + } + byte[] out = new byte[len >> 1]; + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + return out; + } + + protected static int toDigit(char ch, int index) { + int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new RuntimeException("非法16进制字符 " + ch + + " 在索引 " + index); + } + return digit; + } + + private boolean isServiceRunning() { + ActivityManager activityManager = (ActivityManager) this.getApplicationContext() + .getSystemService(Context.ACTIVITY_SERVICE); + ComponentName serviceName = new ComponentName("com.yaheen.fayconnectordemo", ".FayConnectorService"); + PendingIntent intent = activityManager.getRunningServiceControlPanel(serviceName); + if (intent == null){ + return false; + } + return true; + + } +} \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/fayConnectorDemo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/drawable-v24/icon.png b/fayConnectorDemo/app/src/main/res/drawable-v24/icon.png new file mode 100644 index 0000000..c66c4d3 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/drawable-v24/icon.png differ diff --git a/fayConnectorDemo/app/src/main/res/drawable/ic_launcher_background.xml b/fayConnectorDemo/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fayConnectorDemo/app/src/main/res/layout/activity_main.xml b/fayConnectorDemo/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..aa2e573 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + diff --git a/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/fayConnectorDemo/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/fayConnectorDemo/app/src/main/res/values-night/themes.xml b/fayConnectorDemo/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..3fe0a9a --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/values/colors.xml b/fayConnectorDemo/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/values/strings.xml b/fayConnectorDemo/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..6fdadf3 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + fayConnectorDemo + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/values/themes.xml b/fayConnectorDemo/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..215bbd4 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/xml/backup_rules.xml b/fayConnectorDemo/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/main/res/xml/data_extraction_rules.xml b/fayConnectorDemo/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/fayConnectorDemo/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/fayConnectorDemo/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java b/fayConnectorDemo/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java new file mode 100644 index 0000000..1eee6ff --- /dev/null +++ b/fayConnectorDemo/app/src/test/java/com/yaheen/fayconnectordemo/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.yaheen.fayconnectordemo; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/fayConnectorDemo/build.gradle b/fayConnectorDemo/build.gradle new file mode 100644 index 0000000..a550ce3 --- /dev/null +++ b/fayConnectorDemo/build.gradle @@ -0,0 +1,9 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.2.1' apply false + id 'com.android.library' version '7.2.1' apply false +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/fayConnectorDemo/gradle.properties b/fayConnectorDemo/gradle.properties new file mode 100644 index 0000000..dab7c28 --- /dev/null +++ b/fayConnectorDemo/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/fayConnectorDemo/gradle/wrapper/gradle-wrapper.jar b/fayConnectorDemo/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/fayConnectorDemo/gradle/wrapper/gradle-wrapper.jar differ diff --git a/fayConnectorDemo/gradle/wrapper/gradle-wrapper.properties b/fayConnectorDemo/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3d3b352 --- /dev/null +++ b/fayConnectorDemo/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jan 20 09:27:45 CST 2023 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/fayConnectorDemo/gradlew b/fayConnectorDemo/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/fayConnectorDemo/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/fayConnectorDemo/gradlew.bat b/fayConnectorDemo/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/fayConnectorDemo/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/fayConnectorDemo/settings.gradle b/fayConnectorDemo/settings.gradle new file mode 100644 index 0000000..49e31ef --- /dev/null +++ b/fayConnectorDemo/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "fayConnectorDemo" +include ':app'