package com.skydoves.magiclight_ble_control.views.activity; import android.Manifest; import android.app.Activity; import android.bluetooth.BluetoothGattCharacteristic; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Toast; import com.skydoves.colorpickerview.ColorPickerView; import com.skydoves.magiclight_ble_control.R; import com.skydoves.magiclight_ble_control.bleCommunication.BluetoothGattAttributes; import com.skydoves.magiclight_ble_control.bleCommunication.BluetoothLeService; import com.skydoves.magiclight_ble_control.data.DeviceInfoManager; import com.skydoves.magiclight_ble_control.otto.BusProvider; import com.skydoves.magiclight_ble_control.otto.DeviceChangedEvent; import com.skyfishjy.library.RippleBackground; import com.squareup.otto.Subscribe; import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar; import java.util.Random; import be.tarsos.dsp.AudioDispatcher; import be.tarsos.dsp.AudioEvent; import be.tarsos.dsp.AudioProcessor; import be.tarsos.dsp.io.android.AudioDispatcherFactory; import be.tarsos.dsp.pitch.PitchDetectionHandler; import be.tarsos.dsp.pitch.PitchDetectionResult; import be.tarsos.dsp.pitch.PitchProcessor; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; /** * Created by skydoves on 2017-07-01. */ public class MainActivity extends AppCompatActivity { private final String TAG = MainActivity.this.getClass().getSimpleName(); private boolean mConnected = false; private BluetoothLeService mBluetoothLeService; private static final int REQUEST_WRITE_STORAGE = 8000; private static final int RESULT_LOAD_IMAGE = 8001; private byte[] ledrgb = new byte[3]; private byte ledbright = (byte)0XFF; private int sensitive = 250; private long lastPitch = 1; private long minPitch = 900; private Thread listeningThread; private Handler uiThread; private AudioDispatcher dispatcher; private AudioProcessor processor; @Bind(R.id.colorPickerView) ColorPickerView colorPickerView; @Bind(R.id.seekBar) DiscreteSeekBar discreteSeekBar; @Bind(R.id.seekBar_sensitive) DiscreteSeekBar sensitivebar; @Bind(R.id.ripple) RippleBackground rippleBackground; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); BusProvider.getInstance().register(this); // request ble permission requestPermission(); // connection ble service Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); // set Listeners colorPickerView.setColorListener(colorListener); discreteSeekBar.setOnProgressChangeListener(progressChangeListener); discreteSeekBar.setMax(255); discreteSeekBar.setProgress(255); sensitivebar.setOnProgressChangeListener(progressChangeListener_sensitive); sensitivebar.setMax(100); sensitivebar.setProgress(sensitive/10); // connect ble device if (mBluetoothLeService != null) mBluetoothLeService.connect(DeviceInfoManager.getInstance().getDeviceAddress()); } /** * bluetooth service connection */ private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder service) { mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); if (!mBluetoothLeService.initialize()) { Toast.makeText(getBaseContext(), R.string.ble_not_find, Toast.LENGTH_SHORT).show(); finish(); } mBluetoothLeService.connect(DeviceInfoManager.getInstance().getDeviceAddress()); } @Override public void onServiceDisconnected(ComponentName componentName) { mBluetoothLeService = null; } }; /** * receive connection state */ private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final Intent mIntent = intent; final String action = intent.getAction(); // connected if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { Log.e(TAG, "BroadcastReceiver : Connected!"); mConnected = true; Toast.makeText(getBaseContext(), R.string.ble_connect_success, Toast.LENGTH_SHORT).show(); } // disconnected else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { Log.e(TAG, "BroadcastReceiver : Disconnected!"); mConnected = false; Toast.makeText(getBaseContext(), R.string.ble_disconnected, Toast.LENGTH_SHORT).show(); } // found GATT service else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { Log.e(TAG, "BroadcastReceiver : Found GATT!"); } } }; /** * get broadcast intent-filter * @return */ private static IntentFilter makeGattUpdateIntentFilter() { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); return intentFilter; } @Override protected void onResume() { super.onResume(); registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); } @Override protected void onPause() { super.onPause(); unregisterReceiver(mGattUpdateReceiver); } @Override protected void onDestroy() { super.onDestroy(); BusProvider.getInstance().unregister(this); try { if (mConnected) { unbindService(mServiceConnection); mBluetoothLeService = null; } } catch (Exception e){ Log.e(TAG, "BLE unbind Error"); } if(listeningThread != null) { dispatcher.removeAudioProcessor(processor); listeningThread.interrupt(); } } /** * if selected a new ble device, connect a new one * @param event */ @Subscribe public void deviceChanged(DeviceChangedEvent event) { if (mBluetoothLeService != null) { mBluetoothLeService.disconnect(); new Handler().postDelayed(new Runnable() { @Override public void run() { mBluetoothLeService.connect(DeviceInfoManager.getInstance().getDeviceAddress()); } }, 500); } else mBluetoothLeService.connect(DeviceInfoManager.getInstance().getDeviceAddress()); } /** * send rgb byte array to ble device * @param rgb * @return */ private boolean controlLed(byte[] rgb) { // get bluetoothGattCharacteristic BluetoothGattCharacteristic characteristic = mBluetoothLeService.getGattCharacteristic(BluetoothGattAttributes.LED_CHARACTERISTIC); if (characteristic != null) { // check connection if (!mConnected) { Toast.makeText(this, R.string.ble_not_connected, Toast.LENGTH_SHORT).show(); return false; } // send characteristic data mBluetoothLeService.sendDataCharacteristic(characteristic, rgb); return true; } else Log.e(TAG, "Not founded characteristic"); return false; } /** * colorPickerView color listener */ private ColorPickerView.ColorListener colorListener = newColor -> { if(mConnected) { byte[] rgb = getLedBytes(newColor); controlLed(rgb); for(int i=0; i<3; i++) ledrgb[i] = rgb[i+1]; } }; /** * get rgb byte array * @param newColor new color value * @return */ private byte[] getLedBytes(int newColor) { byte[] rgb = new byte[5]; int color = (int)Long.parseLong(String.format("%06X", (0xFFFFFF & newColor)), 16); rgb[0] = (byte)0xA1; rgb[1] = (byte)((color >> 16) & 0xFF); rgb[2]= (byte)((color >> 8) & 0xFF); rgb[3] = (byte)((color >> 0) & 0xFF); rgb[4] = ledbright; return rgb; } /** * discreteBar change listener */ private DiscreteSeekBar.OnProgressChangeListener progressChangeListener = new DiscreteSeekBar.OnProgressChangeListener() { @Override public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) { if(mConnected) { byte[] rgb = new byte[5]; rgb[0] = (byte)0xA1; rgb[1] = ledrgb[0]; rgb[2] = ledrgb[1]; rgb[3] = ledrgb[2]; rgb[4] = ledbright; controlLed(rgb); ledbright = (byte)(value & 0xFF); } } @Override public void onStartTrackingTouch(DiscreteSeekBar seekBar) { } @Override public void onStopTrackingTouch(DiscreteSeekBar seekBar) { } }; private DiscreteSeekBar.OnProgressChangeListener progressChangeListener_sensitive = new DiscreteSeekBar.OnProgressChangeListener() { @Override public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) { sensitive = value; } @Override public void onStartTrackingTouch(DiscreteSeekBar seekBar) { } @Override public void onStopTrackingTouch(DiscreteSeekBar seekBar) { } }; @OnClick(R.id.palette) public void btn_Palette(View v) { Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(intent, RESULT_LOAD_IMAGE); } @OnClick(R.id.music) public void btn_Music(View v) { if(!rippleBackground.isRippleAnimationRunning()) { startDispatch(); rippleBackground.startRippleAnimation(); sensitivebar.setVisibility(View.VISIBLE); Toast.makeText(this, "music start!", Toast.LENGTH_SHORT).show(); } else if (listeningThread != null){ dispatcher.removeAudioProcessor(processor); listeningThread.interrupt(); rippleBackground.stopRippleAnimation(); sensitivebar.setVisibility(View.GONE); Toast.makeText(this, "music stop!", Toast.LENGTH_SHORT).show(); } } @OnClick(R.id.bluetooth) public void btn_Bluetooth(View v) { Intent intent = new Intent(this, SelectDeviceActivity.class); startActivity(intent); } private void startDispatch() { dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050, 1024, 0); uiThread = new Handler(); PitchDetectionHandler pdh = (PitchDetectionResult result, AudioEvent audioEven) -> uiThread.post(() -> { final float pitchInHz = result.getPitch(); int pitch = pitchInHz > 0 ? (int) pitchInHz : 1; if(pitch > 1 && mConnected) { if((pitch - lastPitch) >= sensitive * 10) { Random random = new Random(); byte[] rgb = getLedBytes(random.nextInt(600000000) + 50000); controlLed(rgb); } if(minPitch > pitch) minPitch = pitch; } lastPitch = pitch; }); processor = new PitchProcessor(PitchProcessor.PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, pdh); dispatcher.addAudioProcessor(processor); listeningThread = new Thread(dispatcher); listeningThread.start(); } /** * requestPermissions */ private void requestPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, R.string.permission_request, Toast.LENGTH_LONG); ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_WRITE_STORAGE); } } /** * onActivityResult, request ble system enable * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // user chose not to enable bluetooth. if (requestCode == REQUEST_WRITE_STORAGE && resultCode == Activity.RESULT_CANCELED) { Toast.makeText(this, R.string.ble_canceled, Toast.LENGTH_SHORT).show(); finish(); return; } // user choose a picture from gallery else if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) { Uri selectedImage = data.getData(); String[] filePathColumn = { MediaStore.Images.Media.DATA }; Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); Bitmap bitmap = BitmapFactory.decodeFile(picturePath); Drawable drawable = new BitmapDrawable(getResources(), bitmap); colorPickerView.setPaletteDrawable(drawable); } } }