359 lines
14 KiB
Java
359 lines
14 KiB
Java
|
/*
|
||
|
* Copyright (c) 2018-2020 DJI
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
package com.dji.ux.beta.sample;
|
||
|
|
||
|
import android.Manifest;
|
||
|
import android.content.BroadcastReceiver;
|
||
|
import android.content.Context;
|
||
|
import android.content.Intent;
|
||
|
import android.content.IntentFilter;
|
||
|
import android.content.SharedPreferences;
|
||
|
import android.content.pm.PackageManager;
|
||
|
import android.os.AsyncTask;
|
||
|
import android.os.Build;
|
||
|
import android.os.Bundle;
|
||
|
import android.util.Log;
|
||
|
import android.widget.TextView;
|
||
|
import android.widget.Toast;
|
||
|
|
||
|
import androidx.annotation.NonNull;
|
||
|
import androidx.annotation.RequiresApi;
|
||
|
import androidx.appcompat.app.AppCompatActivity;
|
||
|
import androidx.core.app.ActivityCompat;
|
||
|
import androidx.core.content.ContextCompat;
|
||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||
|
|
||
|
import com.dji.ux.beta.sample.cameraview.CameraActivity;
|
||
|
import com.dji.ux.beta.sample.connection.TcpClientService;
|
||
|
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||
|
import com.ncorti.slidetoact.SlideToActView;
|
||
|
|
||
|
import org.jetbrains.annotations.NotNull;
|
||
|
|
||
|
import java.text.SimpleDateFormat;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Calendar;
|
||
|
import java.util.List;
|
||
|
import java.util.Locale;
|
||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||
|
|
||
|
import butterknife.BindView;
|
||
|
import butterknife.ButterKnife;
|
||
|
import dji.common.error.DJIError;
|
||
|
import dji.common.error.DJISDKError;
|
||
|
import dji.sdk.base.BaseComponent;
|
||
|
import dji.sdk.base.BaseProduct;
|
||
|
import dji.sdk.products.Aircraft;
|
||
|
import dji.sdk.sdkmanager.DJISDKInitEvent;
|
||
|
import dji.sdk.sdkmanager.DJISDKManager;
|
||
|
|
||
|
/**
|
||
|
* Handles the connection to the product and provides links to the different test activities. Also
|
||
|
* shows the current connection state and displays logs for the different steps of the SDK
|
||
|
* registration process.
|
||
|
*/
|
||
|
//@RequiresApi(api = Build.VERSION_CODES.S)
|
||
|
public class MainActivity extends AppCompatActivity {
|
||
|
|
||
|
// SharedPreferences sharedPreferences;
|
||
|
|
||
|
//region Constants
|
||
|
private static final int REQUEST_PERMISSION_CODE = 12345;
|
||
|
private static final String[] REQUIRED_PERMISSION_LIST = new String[]{
|
||
|
Manifest.permission.VIBRATE, // Gimbal rotation
|
||
|
Manifest.permission.INTERNET, // API requests
|
||
|
Manifest.permission.ACCESS_WIFI_STATE, // WIFI connected products
|
||
|
Manifest.permission.ACCESS_COARSE_LOCATION, // Maps
|
||
|
Manifest.permission.ACCESS_NETWORK_STATE, // WIFI connected products
|
||
|
Manifest.permission.ACCESS_FINE_LOCATION, // Maps
|
||
|
Manifest.permission.CHANGE_WIFI_STATE, // Changing between WIFI and USB connection
|
||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE, // Log files
|
||
|
Manifest.permission.BLUETOOTH, // Bluetooth connected products
|
||
|
Manifest.permission.BLUETOOTH_ADMIN, // Bluetooth connected products
|
||
|
Manifest.permission.READ_EXTERNAL_STORAGE, // Log files
|
||
|
Manifest.permission.READ_PHONE_STATE, // Device UUID accessed upon registration
|
||
|
Manifest.permission.RECORD_AUDIO, // Speaker accessory
|
||
|
//Manifest.permission.BLUETOOTH_SCAN,
|
||
|
//Manifest.permission.BLUETOOTH_CONNECT,
|
||
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||
|
};
|
||
|
private static final String TIME_FORMAT = "MMM dd, yyyy 'at' h:mm:ss a";
|
||
|
private static final String TAG = "MainActivity";
|
||
|
private static final String ACTION = "FROM_MAIN";
|
||
|
|
||
|
//TODO: eliminare broadcast receiver e prendere indirizzo ip server dallo shared preference
|
||
|
//TODO: inviare al server stato iniziale connessione del drone
|
||
|
//TODO: applicazione crasha se Bluetooth acceso (Android 12 bug)
|
||
|
//TODO: eliminare il toast del log
|
||
|
|
||
|
//endregion
|
||
|
private static boolean isAppStarted = false;
|
||
|
@BindView(R.id.text_view_version)
|
||
|
protected TextView versionTextView;
|
||
|
@BindView(R.id.text_view_registered)
|
||
|
protected TextView registeredTextView;
|
||
|
@BindView(R.id.text_view_product_name)
|
||
|
protected TextView productNameTextView;
|
||
|
@BindView(R.id.text_view_server_ip)
|
||
|
protected TextView ipServerTextView;
|
||
|
@BindView(R.id.camera_button)
|
||
|
protected SlideToActView cameraButton;
|
||
|
//region Fields
|
||
|
private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false);
|
||
|
private int lastProgress = -1;
|
||
|
private DJISDKManager.SDKManagerCallback registrationCallback = new DJISDKManager.SDKManagerCallback() {
|
||
|
|
||
|
@Override
|
||
|
public void onRegister(DJIError error) {
|
||
|
isRegistrationInProgress.set(false);
|
||
|
if (error == DJISDKError.REGISTRATION_SUCCESS) {
|
||
|
DJISDKManager.getInstance().startConnectionToProduct();
|
||
|
runOnUiThread(() -> {
|
||
|
registeredTextView.setText(R.string.registered);
|
||
|
});
|
||
|
Log.i(TAG, "Registration success");
|
||
|
} else {
|
||
|
showToast( "Register sdk fails, check network is available");
|
||
|
Log.i(TAG, "Registration failed");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onProductDisconnect() {
|
||
|
runOnUiThread(() -> {
|
||
|
//addLog("Disconnected from product");
|
||
|
productNameTextView.setText(R.string.no_product);
|
||
|
fromActivityToService(ACTION, "drone_connection", getString(R.string.no_product));
|
||
|
});
|
||
|
Log.i(TAG, "Drone disconnected");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onProductConnect(BaseProduct product) {
|
||
|
if (product != null) {
|
||
|
runOnUiThread(() -> {
|
||
|
//addLog("Connected to product");
|
||
|
if (product.getModel() != null) {
|
||
|
productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName()));
|
||
|
fromActivityToService(ACTION, "drone_connection", getString(R.string.product_name, product.getModel().getDisplayName()));
|
||
|
} else if (product instanceof Aircraft) {
|
||
|
Aircraft aircraft = (Aircraft) product;
|
||
|
if (aircraft.getRemoteController() != null) {
|
||
|
productNameTextView.setText(getString(R.string.remote_controller));
|
||
|
fromActivityToService(ACTION, "drone_connection", getString((R.string.remote_controller)));
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
Log.i(TAG, String.format("onProductConnect newProduct:%s", product));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onProductChanged(BaseProduct product) {
|
||
|
if (product != null) {
|
||
|
runOnUiThread(() -> {
|
||
|
//addLog("Product changed");
|
||
|
if (product.getModel() != null) {
|
||
|
productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName()));
|
||
|
} else if (product instanceof Aircraft) {
|
||
|
Aircraft aircraft = (Aircraft) product;
|
||
|
if (aircraft.getRemoteController() != null) {
|
||
|
productNameTextView.setText(getString(R.string.remote_controller));
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onComponentChange(BaseProduct.ComponentKey key,
|
||
|
BaseComponent oldComponent,
|
||
|
BaseComponent newComponent) {
|
||
|
Log.i(TAG, key.toString() + " changed");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int totalProcess) {
|
||
|
Log.i(TAG, djisdkInitEvent.getInitializationState().toString());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void onDatabaseDownloadProgress(long current, long total) {
|
||
|
runOnUiThread(() -> {
|
||
|
int progress = (int) (100 * current / total);
|
||
|
if (progress == lastProgress) {
|
||
|
return;
|
||
|
}
|
||
|
lastProgress = progress;
|
||
|
//addLog("Fly safe database download progress: " + progress);
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
private List<String> missingPermission = new ArrayList<>();
|
||
|
//endregion
|
||
|
|
||
|
/**
|
||
|
* Whether the app has started.
|
||
|
*
|
||
|
* @return `true` if the app has been started.
|
||
|
*/
|
||
|
public static boolean isStarted() {
|
||
|
return isAppStarted;
|
||
|
}
|
||
|
|
||
|
//region Lifecycle
|
||
|
@Override
|
||
|
protected void onCreate(Bundle savedInstanceState) {
|
||
|
super.onCreate(savedInstanceState);
|
||
|
setContentView(R.layout.activity_main);
|
||
|
ButterKnife.bind(this);
|
||
|
isAppStarted = true;
|
||
|
checkAndRequestPermissions();
|
||
|
versionTextView.setText(getResources().getString(R.string.sdk_version,
|
||
|
DJISDKManager.getInstance().getSDKVersion()));
|
||
|
|
||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||
|
startForegroundService(new Intent(this, TcpClientService.class));
|
||
|
} else {
|
||
|
startService(new Intent(this, TcpClientService.class));
|
||
|
}
|
||
|
|
||
|
cameraButton.setOnSlideCompleteListener(new SlideToActView.OnSlideCompleteListener() {
|
||
|
@Override
|
||
|
public void onSlideComplete(@NotNull SlideToActView slideToActView) {
|
||
|
Intent intent = new Intent(MainActivity.this, CameraActivity.class);
|
||
|
startActivity(intent);
|
||
|
cameraButton.resetSlider();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
//endregion
|
||
|
|
||
|
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||
|
@Override
|
||
|
public void onReceive(Context context, Intent intent) {
|
||
|
/*
|
||
|
if("FROM_SERVER".equals(intent.getAction())){
|
||
|
String message = intent.getStringExtra("speed_wp");
|
||
|
addLog(message);
|
||
|
}*/
|
||
|
if ("IP".equals(intent.getAction())){
|
||
|
String ip = intent.getStringExtra("server_ip");
|
||
|
ipServerTextView.setText("Status: " + ip);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
private void fromActivityToService(String action, String key, String msg){
|
||
|
Intent intent = new Intent(action);
|
||
|
intent.putExtra(key, msg);
|
||
|
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onResume() {
|
||
|
super.onResume();
|
||
|
IntentFilter filter = new IntentFilter("IP");
|
||
|
filter.addAction("FROM_SERVER");
|
||
|
LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(broadcastReceiver, filter);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onDestroy() {
|
||
|
// Prevent memory leak by releasing DJISDKManager's references to this activity
|
||
|
if (DJISDKManager.getInstance() != null) {
|
||
|
DJISDKManager.getInstance().destroy();
|
||
|
}
|
||
|
isAppStarted = false;
|
||
|
super.onDestroy();
|
||
|
stopService(new Intent(this, TcpClientService.class));
|
||
|
LocalBroadcastManager.getInstance(MainActivity.this).unregisterReceiver(broadcastReceiver);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if there is any missing permissions, and
|
||
|
* requests runtime permission if needed.
|
||
|
*/
|
||
|
private void checkAndRequestPermissions() {
|
||
|
// Check for permissions
|
||
|
for (String eachPermission : REQUIRED_PERMISSION_LIST) {
|
||
|
if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) {
|
||
|
missingPermission.add(eachPermission);
|
||
|
}
|
||
|
}
|
||
|
// Request for missing permissions
|
||
|
if (missingPermission.isEmpty()) {
|
||
|
startSDKRegistration();
|
||
|
} else {
|
||
|
ActivityCompat.requestPermissions(this,
|
||
|
missingPermission.toArray(new String[missingPermission.size()]),
|
||
|
REQUEST_PERMISSION_CODE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Result of runtime permission request
|
||
|
*/
|
||
|
@Override
|
||
|
public void onRequestPermissionsResult(int requestCode,
|
||
|
@NonNull String[] permissions,
|
||
|
@NonNull int[] grantResults) {
|
||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||
|
// Check for granted permission and remove from missing list
|
||
|
if (requestCode == REQUEST_PERMISSION_CODE) {
|
||
|
for (int i = grantResults.length - 1; i >= 0; i--) {
|
||
|
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||
|
missingPermission.remove(permissions[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If there is enough permission, we will start the registration
|
||
|
if (missingPermission.isEmpty()) {
|
||
|
startSDKRegistration();
|
||
|
} else {
|
||
|
ToastUtils.setResultToToast("Missing permissions! Will not register SDK to connect to aircraft.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start the SDK registration
|
||
|
*/
|
||
|
private void startSDKRegistration() {
|
||
|
if (isRegistrationInProgress.compareAndSet(false, true)) {
|
||
|
Log.i(TAG, "registering product");
|
||
|
//addLog("Registering product");
|
||
|
AsyncTask.execute(() -> DJISDKManager.getInstance().registerApp(getApplicationContext(), registrationCallback));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void showToast(final String toastMsg) {
|
||
|
runOnUiThread(new Runnable() {
|
||
|
@Override
|
||
|
public void run() {
|
||
|
Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|