i had created invite users to the app using Facebook in Android, for this used Facebook SDK and added the code given by so peoples, here are my codes
final ImageView facebook1 = (ImageView) findViewById(R.id.facebook1);
facebook1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){
// Perform action on click
Facebook facebook1 = new Facebook("APP_ID");
Bundle paramsOut = new Bundle(), paramsIn = this.getIntent().getExtras();
paramsOut.putString("message", paramsIn.getString("message"));
Singlemenuitem.this.mFacebook.dialog(this, "apprequests", paramsOut, new InviteListener(this));
mFacebook.dialog(Singlemenuitem.this, "apprequests", params, new DialogListener() {
public void onComplete(Bundle values) {
final String returnId = values.getString("request");
if (returnId != null) {
Toast.makeText(getApplicationContext(),
"Request sent " + returnId,
Toast.LENGTH_SHORT).show();
}
}
public void onFacebookError(FacebookError error) {}
public void onError(DialogError e) {}
public void onCancel() {}
});
}
but in the place of "InviteListener" getting an error to create class, if the class is created then also getting error. any guidance pls?
Bundle iviteBundleparams = new Bundle();
iviteBundleparams.putString("message",
"invite message");
//TODO:// if you have friend id then you can pass friend id and the request will send this particular friend id
//myIviteBundleparams.putString("to",
friendId);
final ImageView facebook1 = (ImageView) findViewById(R.id.facebook1);
facebook1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
try {
mFacebook.dialog(context,
"apprequests", iviteBundleparams,
new AppRequestsListener());
}
} catch (Exception e) {
// Log.e("VIAMO_FRIENDS", "" + e.toString());
e.printStackTrace();
}
}
});
/*
* callback for the apprequests dialog which sends an app request to user's
* friends.
*/
public class AppRequestsListener extends BaseDialogListener {
/* Default constructor definition */
public AppRequestsListener() {
// TODO Auto-generated constructor stub
}
public void onComplete(Bundle values) {
if (values.size() < 1) {
Toast toast = Toast.makeText(getApplicationContext(),
"App request cancelled", Toast.LENGTH_SHORT);
toast.show();
} else {
Toast toast = Toast.makeText(getApplicationContext(),
"App request sent", Toast.LENGTH_LONG);
toast.show();
}
}
public void onFacebookError(FacebookError error) {
Toast.makeText(getApplicationContext(),
"Facebook Error: " + error.getMessage(), Toast.LENGTH_SHORT)
.show();
}
public void onCancel() {
Toast toast = Toast.makeText(getApplicationContext(),
"App request cancelled", Toast.LENGTH_SHORT);
toast.show();
}
}
//here mFacebook is your Facebook object
//Facebook mFacebook = new Facebook(APP_ID);
Related
I have included SHA1, and SHA-256 in firebase project.
while fragment is running an Exception Toast Message appears: "This request is missing a valid app indenitier, meaning that neither SafetyNet checks nor reCAPTCHA checks succeeded. Please try again, or checkc the logcat for more details".
Code:
public class VerifyPhoneFragment extends Fragment {
public static String source = null;
PinView pinView;
String phoneNo;
boolean verified = false;
private String VerificationCodSent;
private String CodeEnteredByUser;
public VerifyPhoneFragment() {
// Required empty public constructor
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState){
final NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
final ProgressBar progressBar = view.findViewById(R.id.progress_bar);
ImageView backArrowImageView = view.findViewById(R.id.back_arrow);
TextView backTextView = view.findViewById(R.id.back_text_view);
TextView otpMobileTextView = view.findViewById(R.id.otp_mobile_tv);
Button verifyButton = view.findViewById(R.id.verify_button);
pinView = view.findViewById(R.id.pin_view);
if(source.equals("SignUpFragment")){
phoneNo = "+" + SignUpFragment.mobileTxt;
}
else if(source.equals("LoginFragment")){
phoneNo = "+" + LoginFragment.mobileTxt;
}
else if(source.equals("ForgotPasswordFragment")){
phoneNo = "+" + ForgotPasswordFragment.mobileTxt;
}
otpMobileTextView.setText("Enter OTP sent to Your Phone" + "\n" + phoneNo);
backArrowImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
navController.navigate(R.id.verifyPhone_to_login);
}
});
backTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
navController.navigate(R.id.verifyPhone_to_login);
}
});
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNo,
60,
TimeUnit.SECONDS,
getActivity(),
new PhoneAuthProvider.OnVerificationStateChangedCallbacks(){
#Override
public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
VerificationCodSent = phoneAuthCredential.getSmsCode();
if(VerificationCodSent != null){
progressBar.setVisibility(View.VISIBLE);
verifyButton.setVisibility(view.INVISIBLE);
PhoneAuthCredential phoneAuthCredential1 = PhoneAuthProvider.getCredential(
VerificationCodSent,
CodeEnteredByUser
);
FirebaseAuth.getInstance().signInWithCredential(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
progressBar.setVisibility(View.GONE);
verifyButton.setVisibility(View.VISIBLE);
if(task.isSuccessful()){
verified = true;
}
else{
Toast.makeText(getContext(), "the Verification Code entered was invalid", Toast.LENGTH_LONG).show();
verified = false;
}
}
});
}
}
#Override
public void onVerificationFailed(#NonNull FirebaseException e) {
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
#Override
public void onCodeSent(#NonNull String VerificationCodeSent, #NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
VerificationCodSent = VerificationCodeSent;
}
}
);
verifyButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(pinView.getText().toString().isEmpty()){
Toast.makeText(getContext(), "Please enter a valid verification code", Toast.LENGTH_LONG).show();
verified = false;
goToAppropriateFragment(source);
}
CodeEnteredByUser = pinView.getText().toString();
if(VerificationCodSent != null){
progressBar.setVisibility(View.VISIBLE);
verifyButton.setVisibility(view.INVISIBLE);
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(
VerificationCodSent,
CodeEnteredByUser
);
FirebaseAuth.getInstance().signInWithCredential(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
progressBar.setVisibility(View.GONE);
verifyButton.setVisibility(View.VISIBLE);
if(task.isSuccessful()){
verified = true;
}
else{
Toast.makeText(getContext(), "the Verification Code entered was invalid", Toast.LENGTH_LONG).show();
verified = false;
}
}
});
}
// afterVerification
goToAppropriateFragment(source);
}
});
}
public void goToAppropriateFragment(String source){
final NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
if(source.equals("SignUpFragment")){
if(verified){
DatabaseHelper databaseHelper = new DatabaseHelper(getContext());
if(databaseHelper.addNewUser()){ // if the user added successfully addUser() returns true
Toast.makeText(getContext(), "You have signed up successfully! you can login", Toast.LENGTH_LONG).show();
// go to LoginFragment
navController.navigate(R.id.verifyPhone_to_login);
}
else{ // to be deleted won't arrive here if email is already used
Toast.makeText(getContext(), "Sorry, Unable to sign you up! Try again later", Toast.LENGTH_LONG).show();
}
}
else {
// Toast.makeText(getContext(), "Sorry, it seems that you have not entered the OTP correctly. Try again", Toast.LENGTH_LONG).show();
// go to SignUpFragment
navController.navigate(R.id.verifyPhone_to_signUp);
}
}
else if(source.equals("LoginFragment")){
if(verified){
// go to MainContentActivity
Toast.makeText(getContext(), "Welcome back " + LoginFragment.fNameTxt + "!", Toast.LENGTH_LONG).show();
Intent intent = new Intent(getContext(), MainContentActivity.class);
startActivity(intent);
}
else {
// Toast.makeText(getContext(), "Sorry, it seems that you have not entered the OTP correctly. Try again", Toast.LENGTH_LONG).show();
// go to LoginFragment
navController.navigate(R.id.verifyPhone_to_login);
}
}
else if(source.equals("ForgotPasswordFragment")){
if(verified){
// go to ResetPasswordFragment
navController.navigate(R.id.verifyPhone_to_resetPassword);
}
else {
Toast.makeText(getContext(), "Sorry, it seems that you have not entered the OTP correctly. Try again", Toast.LENGTH_LONG).show();
// go to LoginFragment
navController.navigate(R.id.verifyPhone_to_login);
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_verify_phone, container, false);
}
}
Logcat:
2021-03-30 20:57:21.769 16847-16847/com.example.map_project_v2 E/SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
2021-03-30 20:57:50.049 16847-16847/com.example.map_project_v2 E/zzf: Problem retrieving SafetyNet Token: 7:
2021-03-30 20:57:51.267 16847-17116/com.example.map_project_v2 E/FirebaseAuth: [GetAuthDomainTask] Error getting project config. Failed with INVALID_CERT_HASH 400
2021-03-30 20:57:51.381 16847-16847/com.example.map_project_v2 E/zzf: Failed to get reCAPTCHA token with error [There was an error while trying to get your package certificate hash.]- calling backend without app verification
2021-03-30 20:57:51.763 16847-16976/com.example.map_project_v2 E/FirebaseAuth: [SmsRetrieverHelper] SMS verification code request failed: unknown status code: 17093 null
I was getting the same error and I could solved it with the last two steps of the following (make sure you have covered all of them):
Enable Phone option in Sign-in method under Firebase Authentication
Make sure to download and add the latest google-services.json file in your project
Enable Android Device Verification for your firebase project in https://console.cloud.google.com/
Add library implementation "androidx.browser:browser:1.3.0"
https://developer.android.com/jetpack...
Currently i'm implementing the in app update feature from android. I'm using the immediate update method, the problem that I'm facing is that when the UI prompting the user to update shows and the user does not click the update button instead they click the cross button. The UI for the app update just closes and user can continue using the app.
What I want is that when user click the cross button the app immediately closes, until user updates the app then they can use the app as usual. I also uses the java code for the android development.
public class LoginActivity extends AppCompatActivity {
private LoginViewModel loginViewModel;
public static final String MyPREFERENCES = "LoginPrefs" ;
public static final String Name = "nameKey";
public static final String User = "userKey";
public static final String con = "closed";
public static String error = "";
public static int userFlag = 0;
SharedPreferences sharedpreferences;
SharedPreferences.Editor editor;
public TextInputEditText usernameEditText;
public TextInputEditText passwordEditText;
private AppUpdateManager mAppUpdateManager;
private int RC_APP_UPDATE = 999;
private int inAppUpdateType;
private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
private InstallStateUpdatedListener installStateUpdatedListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
loginViewModel = ViewModelProviders.of(this, new LoginViewModelFactory())
.get(LoginViewModel.class);
usernameEditText = findViewById(R.id.user);
passwordEditText = findViewById(R.id.pass);
final Button loginButton = findViewById(R.id.submitBTN);
final TextInputLayout userL = findViewById(R.id.userL);
final TextInputLayout passL = findViewById(R.id.passL);
final JellyToggleButton jtb = findViewById(R.id.jtb);
// Creates instance of the manager.
mAppUpdateManager = AppUpdateManagerFactory.create(this);
// Returns an intent object that you use to check for an update.
appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
//lambda operation used for below listener
//For flexible update
installStateUpdatedListener = installState -> {
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
};
mAppUpdateManager.registerListener(installStateUpdatedListener);
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();
if(userFlag==1){
jtb.setChecked(true);
}
userL.setHint("Enter username");
sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
loginViewModel.getLoginFormState().observe(this, new Observer<LoginFormState>() {
#Override
public void onChanged(#Nullable LoginFormState loginFormState) {
if (loginFormState == null) {
return;
}
loginButton.setEnabled(loginFormState.isDataValid());
if (loginFormState.getUsernameError() != null) {
usernameEditText.setError(getString(loginFormState.getUsernameError()));
loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
}
if (loginFormState.getPasswordError() != null) {
passwordEditText.setError(getString(loginFormState.getPasswordError()));
loginButton.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake));
}
}
});
loginViewModel.getLoginResult().observe(this, new Observer<LoginResult>() {
#Override
public void onChanged(#Nullable LoginResult loginResult) {
if (loginResult == null) {
return;
}
if (loginResult.getError() != null) {
showLoginFailed(loginResult.getError());
}
if (loginResult.getSuccess() != null) {
updateUiWithUser(loginResult.getSuccess());
Intent i = new Intent(LoginActivity.this, user_dashboard.class);
startActivity(i);
}
setResult(Activity.RESULT_OK);
//Complete and destroy login activity once successful
}
});
TextWatcher afterTextChangedListener = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// ignore
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// ignore
}
#Override
public void afterTextChanged(Editable s) {
loginViewModel.loginDataChanged(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
}
};
usernameEditText.addTextChangedListener(afterTextChangedListener);
passwordEditText.addTextChangedListener(afterTextChangedListener);
passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
loginViewModel.login(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
}
return false;
}
});
loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(userFlag==0) {
loginViewModel.login(usernameEditText.getText().toString(),
passwordEditText.getText().toString());
getStaffData();
}
else if(userFlag==1){
loginWorker();
}
}
});
jtb.setOnStateChangeListener(new JellyToggleButton.OnStateChangeListener() {
#Override
public void onStateChange(float process, State state, JellyToggleButton jtb) {
if (state.equals(State.LEFT)) {
userL.setHint("Enter username");
error = "Username cannot be empty";
userFlag = 0;
}
if (state.equals(State.RIGHT)) {
userL.setHint("Enter badge ID");
error = "Badge ID cannot be empty";
userFlag = 1;
}
}
});
}
#Override
protected void onDestroy() {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
finishAndRemoveTask();
super.onDestroy();
}
#Override
protected void onResume() {
try {
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
inAppUpdateType,
this,
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
//For flexible update
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
super.onResume();
}
#Override //For flexible update
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
}
private void updateUiWithUser(LoggedInUserView model) {
String welcome = getString(R.string.welcome);
// TODO : initiate successful logged in experience
Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_LONG).show();
}
private void showLoginFailed(#StringRes Integer errorString) {
Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show();
}
private void getStaffData() {
String username = usernameEditText.getText().toString();
APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
Call<loginList> call3 = apiInterface3.staffData(username);
call3.enqueue(new Callback<loginList>() {
#Override
public void onResponse(Call<loginList> call, Response<loginList> response) {
loginList list = response.body();
if (list!=null && list.getStatusCode()==1) { //response received.
if(list.getStaffList().size()>0){
Log.d("check-in", list.getStatusCode() + " " + list.getStaffList().get(0).getName());
Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();
final String name = list.getStaffList().get(0).getName();
final String badge = list.getStaffList().get(0).getBadge();
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Name,name);
editor.putString(User,badge);
editor.putInt(con,1);
editor.apply();
}
else if(list.getStaffList().size()==0){
}
}
}
#Override
public void onFailure(Call<loginList> call, Throwable t) {
Log.d("fail",t.toString());
}
});
}
private void loginWorker(){
String username = usernameEditText.getText().toString();
String password = passwordEditText.getText().toString();
APIInterface apiInterface3 = APIClient.getClient().create(APIInterface.class);
Call<loginList> call3 = apiInterface3.loginWorker(username,password);
call3.enqueue(new Callback<loginList>() {
#Override
public void onResponse(Call<loginList> call, Response<loginList> response) {
loginList list = response.body();
Log.d("response", response.body().toString());
if (list!=null && list.getStatusCode()==1) { //response received.
if(list.getLoginList().size()>0){
Log.d("check-in", list.getStatusCode() + " " + list.getLoginList().get(0).getName());
Toast.makeText(LoginActivity.this,"Logged in",Toast.LENGTH_SHORT).show();
List<login> item = response.body().getLoginList();
final String name = list.getLoginList().get(0).getName();
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Name,name);
editor.putInt(con,1);
editor.apply();
}
String welcome = getString(R.string.welcome);
Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_SHORT).show();
Intent i = new Intent(LoginActivity.this, user_dashboard.class);
startActivity(i);
}
else
Toast.makeText(LoginActivity.this, "wrong ID or password",Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<loginList> call, Throwable t) {
Log.d("fail",t.toString());
}
});
editor = sharedpreferences.edit();
editor.putString(User, username);
editor.commit();
}
#Override
public void onBackPressed() {
new MaterialAlertDialogBuilder(LoginActivity.this,R.style.MyDialogTheme)
.setTitle("Exit")
.setMessage("Confirm to exit?")
.setBackground(getDrawable(R.drawable.alert_dialog))
// Specifying a listener allows you to take an action before dismissing the dialog.
// The dialog is automatically dismissed when a dialog button is clicked.
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Continue with delete
finishAffinity();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
private void inAppUpdate() {
try {
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
#Override
public void onSuccess(AppUpdateInfo appUpdateInfo) {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
// Request the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
inAppUpdateType,
// The current activity making the update request.
LoginActivity.this,
// Include a request code to later monitor this update request.
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException ignored) {
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void popupSnackbarForCompleteUpdate() {
try {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorL),
"An update has just been downloaded.\nRestart to update",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("INSTALL", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.orange));
snackbar.show();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
}
The image I borrowed from google, on the left image as can be seen there is a cross button on top right user can click to close the update process
The most important point I will emphasize is that you should not force users to update the app until it is absolutely necessary (like some security issues etc). Forcing updates to users is considered a very bad user experience.
To the question you asked, you have the answer in your question itself. If you check the code you have something like this in your onActivityResult method-
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(LoginActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(LoginActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(LoginActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
In case when the user cancels resultCode == RESULT_CANCELED or the update fails resultCode == RESULT_IN_APP_UPDATE_FAILED, you can take whatever action you want. You can finish the activity or whatever is suitable in your situation.
I have created an android application using the DJI SDK. I have followed the instruction, and basically copied the code from the DJI Sample Code (https://github.com/dji-sdk/Mobile-SDK-Android/blob/master/Sample%20Code/app/src/main/java/com/dji/sdk/sample/demo/camera/LiveStreamView.java), since it was working properly.
After launching a Connectivity activity, which registers the SDK and connects to the Mavic 2 Zoom drone, another activity comes, which handles live streaming to a RTMP server. When using the sample code, streaming to the same RTMP server, it has no delay, but when using my app, it has a good 15 second delay. I can't figure out why, I'm using the same components. The only difference is that I'm setting the camera focus to the max, but I did the same in the Sample Code, so it shouldn't cause any problems. Also using the same VideoFeedView as in the Sample.
public class MainActivity extends Activity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getName();
private String liveShowUrl = "rtmp://192.168.00.00/live";
private VideoFeedView primaryVideoFeedView;
private VideoFeedView fpvVideoFeedView;
private EditText showUrlInputEdit;
private Button startLiveShowBtn;
private Button enableVideoEncodingBtn;
private Button disableVideoEncodingBtn;
private Button stopLiveShowBtn;
private Button soundOnBtn;
private Button soundOffBtn;
private Button isLiveShowOnBtn;
private Button showInfoBtn;
private Button showLiveStartTimeBtn;
private Button showCurrentVideoSourceBtn;
private Button changeVideoSourceBtn;
private Camera camera;
private LiveStreamManager.OnLiveChangeListener listener;
private LiveStreamManager.LiveStreamVideoSource currentVideoSource = LiveStreamManager.LiveStreamVideoSource.Primary;
private CommonCallbacks.CompletionCallback focusSetCompletionCallback = new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(DJIError djiError) {
Log.d(TAG, "Camera focus is set to manual");
Toast.makeText(getApplicationContext(), "camera focus set to manual", Toast.LENGTH_SHORT).show();
}
};
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
initListener();
camera = DronifyApplication.getCameraInstance();
camera.getFocusRingValueUpperBound(new CommonCallbacks.CompletionCallbackWith<Integer>() {
#Override
public void onSuccess(Integer integer) {
Toast.makeText(getApplicationContext(), "UPPER IS: " + integer.toString(),Toast.LENGTH_LONG).show();
Log.d(TAG, "UPPER IS: " + integer.toString());
}
#Override
public void onFailure(DJIError djiError) {
Toast.makeText(getApplicationContext(), "UPPER IS NOT SUPPORTED", Toast.LENGTH_LONG).show();
}
});
camera.setFocusMode(SettingsDefinitions.FocusMode.MANUAL, focusSetCompletionCallback);
if (camera.isAdjustableFocalPointSupported()) {
camera.setFocusRingValue(65, new CommonCallbacks.CompletionCallback() {
#Override
public void onResult(DJIError djiError) {
Log.i(TAG, "set focus ring value to max");
Toast.makeText(getApplicationContext(), "set focus ring value to max", Toast.LENGTH_SHORT).show();
}
});
}
Intent intent = new Intent(getApplication(), TCPService.class);
getApplication().startService(intent);
}
#Override
protected void onResume() {
super.onResume();
}
public static boolean isMultiStreamPlatform() {
if (DJISDKManager.getInstance() == null){
return false;
}
Model model = DJISDKManager.getInstance().getProduct().getModel();
return model != null && (model == Model.INSPIRE_2
|| model == Model.MATRICE_200
|| model == Model.MATRICE_210
|| model == Model.MATRICE_210_RTK
|| model == Model.MATRICE_600
|| model == Model.MATRICE_600_PRO
|| model == Model.A3
|| model == Model.N3);
}
private void initUI() {
primaryVideoFeedView = (VideoFeedView) findViewById(R.id.video_view_primary_video_feed);
primaryVideoFeedView.registerLiveVideo(VideoFeeder.getInstance().getPrimaryVideoFeed(), true);
fpvVideoFeedView = (VideoFeedView) findViewById(R.id.video_view_fpv_video_feed);
fpvVideoFeedView.registerLiveVideo(VideoFeeder.getInstance().getSecondaryVideoFeed(), false);
if (isMultiStreamPlatform()){
fpvVideoFeedView.setVisibility(View.VISIBLE);
}
showUrlInputEdit = (EditText) findViewById(R.id.edit_live_show_url_input);
showUrlInputEdit.setText(liveShowUrl);
startLiveShowBtn = (Button) findViewById(R.id.btn_start_live_show);
enableVideoEncodingBtn = (Button) findViewById(R.id.btn_enable_video_encode);
disableVideoEncodingBtn = (Button) findViewById(R.id.btn_disable_video_encode);
stopLiveShowBtn = (Button) findViewById(R.id.btn_stop_live_show);
soundOnBtn = (Button) findViewById(R.id.btn_sound_on);
soundOffBtn = (Button) findViewById(R.id.btn_sound_off);
isLiveShowOnBtn = (Button) findViewById(R.id.btn_is_live_show_on);
showInfoBtn = (Button) findViewById(R.id.btn_show_info);
showLiveStartTimeBtn = (Button) findViewById(R.id.btn_show_live_start_time);
showCurrentVideoSourceBtn = (Button) findViewById(R.id.btn_show_current_video_source);
changeVideoSourceBtn = (Button) findViewById(R.id.btn_change_video_source);
startLiveShowBtn.setOnClickListener(this);
enableVideoEncodingBtn.setOnClickListener(this);
disableVideoEncodingBtn.setOnClickListener(this);
stopLiveShowBtn.setOnClickListener(this);
soundOnBtn.setOnClickListener(this);
soundOffBtn.setOnClickListener(this);
isLiveShowOnBtn.setOnClickListener(this);
showInfoBtn.setOnClickListener(this);
showLiveStartTimeBtn.setOnClickListener(this);
showCurrentVideoSourceBtn.setOnClickListener(this);
changeVideoSourceBtn.setOnClickListener(this);
}
private void initListener() {
showUrlInputEdit.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
liveShowUrl = s.toString();
}
#Override
public void afterTextChanged(Editable s) {
}
});
listener = new LiveStreamManager.OnLiveChangeListener() {
#Override
public void onStatusChanged(int i) {
//Toast.makeText(getApplicationContext(), "status changed : " + i, Toast.LENGTH_SHORT).show();
}
};
}
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
BaseProduct product = DronifyApplication.getProductInstance();
if (product == null || !product.isConnected()) {
//Toast.makeText(getApplicationContext(), "disconnected", Toast.LENGTH_SHORT).show();
return;
}
if (isLiveStreamManagerOn()){
DJISDKManager.getInstance().getLiveStreamManager().registerListener(listener);
}
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (isLiveStreamManagerOn()){
DJISDKManager.getInstance().getLiveStreamManager().unregisterListener(listener);
}
}
private boolean isLiveStreamManagerOn() {
if (DJISDKManager.getInstance().getLiveStreamManager() == null) {
//Toast.makeText(getApplicationContext(), "no liveStream manager", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_live_show:
startLiveShow();
break;
case R.id.btn_enable_video_encode:
enableReEncoder();
break;
case R.id.btn_disable_video_encode:
disableReEncoder();
break;
case R.id.btn_stop_live_show:
stopLiveShow();
break;
case R.id.btn_sound_on:
soundOn();
break;
case R.id.btn_sound_off:
soundOff();
break;
case R.id.btn_is_live_show_on:
isLiveShowOn();
break;
case R.id.btn_show_info:
showInfo();
break;
case R.id.btn_show_live_start_time:
showLiveStartTime();
break;
case R.id.btn_show_current_video_source:
showCurrentVideoSource();
break;
case R.id.btn_change_video_source:
changeVideoSource();
break;
default:
break;
}
}
private void enableReEncoder() {
if (!isLiveStreamManagerOn()) {
return;
}
DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(true);
Toast.makeText(getApplicationContext(), "Force Re-Encoder Enabled!", Toast.LENGTH_SHORT).show();
}
private void disableReEncoder() {
if (!isLiveStreamManagerOn()) {
return;
}
DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(false);
Toast.makeText(getApplicationContext(), "Disable Force Re-Encoder!", Toast.LENGTH_SHORT).show();
}
private void soundOn() {
if (!isLiveStreamManagerOn()) {
return;
}
DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(false);
Toast.makeText(getApplicationContext(), "Sound ON", Toast.LENGTH_SHORT).show();
}
private void soundOff() {
if (!isLiveStreamManagerOn()) {
return;
}
DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(true);
Toast.makeText(getApplicationContext(), "Sound OFF", Toast.LENGTH_SHORT).show();
}
private void isLiveShowOn() {
if (!isLiveStreamManagerOn()) {
return;
}
Toast.makeText(getApplicationContext(), "Is Live Show On:" + DJISDKManager.getInstance().getLiveStreamManager().isStreaming(), Toast.LENGTH_SHORT).show();
}
private void showLiveStartTime() {
if (!isLiveStreamManagerOn()) {
return;
}
if (!DJISDKManager.getInstance().getLiveStreamManager().isStreaming()){
Toast.makeText(getApplicationContext(), "Please Start Live First", Toast.LENGTH_SHORT).show();
return;
}
long startTime = DJISDKManager.getInstance().getLiveStreamManager().getStartTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
String sd = sdf.format(new Date(Long.parseLong(String.valueOf(startTime))));
Toast.makeText(getApplicationContext(), "Live Start Time: " + sd, Toast.LENGTH_SHORT).show();
}
private void changeVideoSource() {
if (!isLiveStreamManagerOn()) {
return;
}
if (!isSupportSecondaryVideo()) {
return;
}
if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
Toast.makeText(getApplicationContext(), "Before change live source, you should stop live stream!", Toast.LENGTH_SHORT).show();
return;
}
currentVideoSource = (currentVideoSource == LiveStreamManager.LiveStreamVideoSource.Primary) ?
LiveStreamManager.LiveStreamVideoSource.Secoundary :
LiveStreamManager.LiveStreamVideoSource.Primary;
DJISDKManager.getInstance().getLiveStreamManager().setVideoSource(currentVideoSource);
Toast.makeText(getApplicationContext(), "Change Success ! Video Source : " + currentVideoSource.name(), Toast.LENGTH_SHORT).show();
}
private void showCurrentVideoSource(){
Toast.makeText(getApplicationContext(), "Video Source : " + currentVideoSource.name(), Toast.LENGTH_SHORT).show();
}
private boolean isSupportSecondaryVideo(){
if (isMultiStreamPlatform()) {
Toast.makeText(getApplicationContext(), "No secondary video!", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
private void showInfo() {
StringBuilder sb = new StringBuilder();
sb.append("Video BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoBitRate()).append(" kpbs\n");
sb.append("Audio BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveAudioBitRate()).append(" kpbs\n");
sb.append("Video FPS:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoFps()).append("\n");
sb.append("Video Cache size:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoCacheSize()).append(" frame");
Toast.makeText(getApplicationContext(), sb.toString(), Toast.LENGTH_LONG).show();
}
void startLiveShow() {
Toast.makeText(getApplicationContext(), "start live show: " + isLiveStreamManagerOn(), Toast.LENGTH_SHORT).show();
if (!isLiveStreamManagerOn()) {
Toast.makeText(getApplicationContext(), "1. return", Toast.LENGTH_SHORT).show();
return;
}
if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
Toast.makeText(getApplicationContext(), "live show already started", Toast.LENGTH_SHORT).show();
return;
}
new Thread() {
#Override
public void run() {
DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl(liveShowUrl);
DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(true);
DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(false);
final int result = DJISDKManager.getInstance().getLiveStreamManager().startStream();
DJISDKManager.getInstance().getLiveStreamManager().setStartTime();
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplication(), "RESULT: " + result, Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
private void stopLiveShow() {
if (!isLiveStreamManagerOn()) {
return;
}
DJISDKManager.getInstance().getLiveStreamManager().stopStream();
Toast.makeText(getApplicationContext(), "stop live show", Toast.LENGTH_SHORT).show();
}
}
Any idea why? I have tested it on Google Pixel 2, and Huawei Mate 10. The sample has no problem on both devices, my app has the delay. Thanks!
Answering my own question, the only difference I noticed was that the SampleCode asked for 4 permission, and all the projects I've tried or copied the permissions, always just 3 permissions.
So Manifest:
< uses-permission android:name="android.permission.RECORD_AUDIO" />
your runtime permissions:
Manifest.permission.RECORD_AUDIO
and the delay is gone, all works fine. Still don't know why :)
When I try to save a ParseObject in my Android app it does not seem to be saving to the Dashboard. There seems to be no error's showing. Here's my code.
N.B: The code is nested within protected void onCreate(Bundle savedInstanceState)
continue_reg.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
firstNameTxt = first_name.getText().toString();
lastNameTxt = last_name.getText().toString();
if (firstNameTxt.equals("") && lastNameTxt.equals("")) {
Toast.makeText(getApplicationContext(), "Please complete all fields", Toast.LENGTH_LONG).show();
} else {
ParseObject patientInfo = new ParseObject("PatientInformation");
patientInfo.put("firstName", firstNameTxt);
patientInfo.put("lastName", lastNameTxt);
patientInfo.saveInBackground();
Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_LONG).show();
}
}
});
Try to use the SaveCallback(), so you can see if any error appears.
patientInfo.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
// No error, the object was saved
Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_LONG).show();
} else {
// Error saving object, print the logs
e.printStackTrace();
}
}
});
I finally figured out how to consume an IAP in v3 of the InAppBilling API. The user can now constantly consume as many products as they please.
Now I want the GUI for the user to be updated once the purchase is confirmed complete. I have put Toasts all over the below code to try and find out where to update the GUI at but I have yet to have a Toast appear yet. But remember that the consuming of the IAPs work.
I have identified in my code below the snippet that updates the GUI for the user. That snippet of code is what I want run AFTER a successful purchase is complete.
So my question is where to put that snippet of code so that the GUI is updated for the user after a successful purchase.
public class Levels extends SwarmActivity {
//static final String SKU_BUYLIVES = "buy5lives";
static final String SKU_BUYLIVES = "android.test.purchased";
IabHelper mHelper;
IInAppBillingService mService;
#Override
public void onCreate(Bundle savedInstanceState) {
moreLives = (Button)findViewById(R.id.moreLives);
moreLives.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
buyLives();
}
});
}
public void buyLives() {
final Dialog dialog = new Dialog(c);
dialog.setContentView(R.layout.buylives);
String base64EncodedPublicKey = a + b + d + e + f + g + h + i + j + k;
TextView title = (TextView)dialog.findViewById(R.id.question);
Button no = (Button)dialog.findViewById(R.id.no);
Button yes = (Button)dialog.findViewById(R.id.yes);
title.setText(c.getResources().getString(R.string.buyLivesQuestion));
no.setText(c.getResources().getString(R.string.maybelater));
yes.setText(c.getResources().getString(R.string.buy));
// Create the helper, passing it our context and the public key to verify signatures with
mHelper = new IabHelper(Levels.this, base64EncodedPublicKey);
// start setup. this is asynchronous and the specified listener will be called once setup completes.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// there was a problem.
complain("An error has occurred. We apologize for the inconvenience. " + c.getResources().getString(R.string.problem1) + " " + result);
return;
}
// IAB is fully set up. Now, let's get an inventory of stuff we own.
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
yes.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
mHelper.launchPurchaseFlow(Levels.this, SKU_BUYLIVES, 10001, mPurchaseFinishedListener, "payload");
dialog.dismiss();
// the below ~14 lines is the code that I want to call to update the GUI for the user. this block of code has been all over the place. this is just the last spot I tested it at.
SharedPreferences settings = getSharedPreferences("level_SP", 0);
livesCount = settings.getInt("livesTotal1", 0);
remainderTimeStamp = settings.getLong("remainderTimeStamp1", 0);
livesCount = 5;
remainderTimeStamp = 0;
SharedPreferences.Editor editor = settings.edit();
editor.putInt("livesTotal1", livesCount);
editor.putLong("remainderTimeStamp1", remainderTimeStamp);
editor.commit();
livesCountTV.setText(c.getResources().getString(R.string.livesCount) + " " + livesCount);
livesCounterTV.setText(c.getResources().getString(R.string.livesCounter) + " FULL!");
}
});
no.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
dialog.dismiss();
}
});
dialog.show();
}
// listener that's called when we finish querying the items and subscriptions we own.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if(result.isFailure()) {
complain(c.getResources().getString(R.string.sorryerror) + c.getResources().getString(R.string.failedtoquery) + " " + result);
return;
} else if(inventory.hasPurchase(SKU_BUYLIVES)) {
mHelper.consumeAsync(inventory.getPurchase(SKU_BUYLIVES), null);
}
}
};
// callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
// this appears to the user immediately after purchasing.
if(result.isFailure()) {
complain(c.getResources().getString(R.string.sorryerror) + result);
} else if(purchase.getSku().equals(SKU_BUYLIVES)) {
alert(c.getResources().getString(R.string.livesbought));
try {
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
// success
Toast.makeText(Levels.this, "SUCCESS", Toast.LENGTH_LONG).show();
try {
mService.consumePurchase(3, getPackageName(), SKU_BUYLIVES);
// this Toast is never seen.
Toast t = Toast.makeText(Levels.this, "PURCHASE CONSUMED", Toast.LENGTH_LONG);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// error
// this Toast is never seen.
Toast.makeText(Levels.this, "ERROR", Toast.LENGTH_LONG).show();
}
} catch (RemoteException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return;
}
};
void complain(String message) {
alert("Error: " + message);
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton("OK", null);
bld.create().show();
}
#Override
public void onDestroy() {
super.onDestroy();
if(mHelper != null) mHelper.dispose();
mHelper = null;
}
}
To open Google Play purchase dialog you should have used startIntentSenderForResult() method with your purchase intent. Once user is done with this dialog, onActivityResult() gets called on your activity. This is the place where you should verify the purchase and update GUI if needed.
This is an example of how you open purchase dialog.
public void buyProduct() {
PendingIntent buyIntent = ... // create your intent here
IntentSender sender = buyIntent.getIntentSender();
try {
startIntentSenderForResult(sender, REQ_BUY_PRODUCT, new Intent(), 0, 0, 0);
} catch (SendIntentException e) {
Log.e(TAG, "", e);
}
}
This is example of how to handle purchase intent
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQ_BUY_PRODUCT && resultCode == Activity.RESULT_OK) {
// here you verify data intent and update your GUI
...
return;
}
}
Both methods belong to your activity.