In-app Billing unlock button only one time - java

private Button clickButton;
private Button buyButton;
private static final String TAG =
"InAppBilling";
IabHelper mHelper;
static final String ITEM_SKU = "tips";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buyButton = (Button)findViewById(R.id.buybutton);
clickButton = (Button)findViewById(R.id.clickbutton);
clickButton.setEnabled(false);
String base64EncodedPublicKey =
" "
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new
IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(TAG, "In-app Billing setup failed: " +
result);
} else {
Log.d(TAG, "In-app Billing is set up OK");
}
}
});
}
public void button2 (View v)
{
Intent intent = new Intent(getApplicationContext(), vtoriFra
gment.class);
startActivity(intent);
}
public void buttonClicked (View view)
{
clickButton.setEnabled(false);
buyButton.setEnabled(true);
Intent intent = new Intent(getApplicationContext(), purviFragment.class);
startActivity(intent);
}
public void buyClick(View view) {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
// Handle error
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
consumeItem();
buyButton.setEnabled(false);
}
}
};
public void consumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),
mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
clickButton.setEnabled(true);
} else {
// handle error
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
#Override
public void onBackPressed() {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
doubleBackToExitPressedOnce=false;
}
}, 2000);
}
}
Hi, i have a trouble mit my app. The case is:
I want when the people pay in my app to unlock one button but i want to pay only one thime but when they pay they unlock the button for only one time. Sorry for my bad english. This is my source code

Your code is a bit messy, but this is your flow:
when the user clicks the button you start the purchasing flow
when when purchasing flow ends, you query for the list of purchased items
if the purchased item exists, you consume it
When you consume an item, you "delete" it, so it is no more on the purchased items list. If you want so sell something just once (for example to remove ads) you don't have to consume it.
Your flow should be this:
query for the purchased items
if the list contains the purchase item, disable the button
if the list doesn't contain che purchase item, enable the button
on click, start the purchase flow
when when purchasing flow ends, you query for the list of purchased items
if the purchased item exists, disable the button and provide the extra feature
if the purchased item doesn't exists, the purchase procedure failed
You should read carefully this page:
https://developer.android.com/training/in-app-billing/purchase-iab-products.html

Related

Java update and delete function is giving me 'java.lang.IllegalStateException'

I am doing my android project and I am using an external API to connect it with the database. I have done the create and view by id. But when it comes to update and deleteI am getting error as E/Failure message: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 2 path $. But I am getting the success message and the the error.
When I delete I am getting this error but when I re build the app the data is deleted. But the update is not working.
This is my external API
//Update fuel details
#PUT("/api/fuel/{id}")
Call<Fuel> updateFuel(#Path("id") int id,#Body Fuel fuel);
//Delete fuel details
#DELETE("/api/fuel/{id}")
Call<Fuel> deleteFuel(#Path("id") int id);
And this is my java class for the update and delete. I have tried for 2 days and still couldn't find a solution for this. Please give me some support. Thank You.
public class ShedFuelUpdateFormActivity extends AppCompatActivity {
EditText fuelType, arrivalTime, arrivalAmount, availableAmount, finishTime;
Button deleteButton, updateButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shed_fuel_update_form_screen);
fuelType = findViewById(R.id.fuelType );
arrivalTime = findViewById(R.id.arrivalTime );
arrivalAmount = findViewById(R.id.arrivalAmount );
availableAmount = findViewById(R.id.availableAmount );
finishTime = findViewById(R.id.finishTime );
deleteButton = findViewById(R.id.deleteButton );
updateButton = findViewById(R.id.updateButton );
Intent intent = getIntent();
int fuelId = Integer.parseInt(intent.getStringExtra("fuelId"));
//Get fuel by id
new Handler(Looper.myLooper()).postDelayed(new Runnable() {
#Override
public void run() {
getFuel(fuelId);
}
}, 1000);
//Call delete function
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
deleteFuel(fuelId);
}
});
//Call update function
updateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
saveFuel(fuelId, updateFuel());
}
});
}
//Get fuel by id details
public void getFuel(int id){
Call<Fuel> getFuelCall = ApiClient.getFuelServices().getFuelById(id);
getFuelCall.enqueue(new Callback<Fuel>() {
#Override
public void onResponse(Call<Fuel> call, Response<Fuel> response) {
if(response.isSuccessful()){
String fuelTypeData = response.body().getFuelType();
String arrivalTimeData = response.body().getFuelArivalTime();
String arrivalAmountData = String.valueOf(response.body().getArivalAmount());
String availableAmountData = String.valueOf(response.body().getCurrentAvailableAmount());
String finishTimeData = response.body().getFuelFinishedTime();
fuelType.setText(fuelTypeData);
arrivalTime.setText(arrivalTimeData);
arrivalAmount.setText(arrivalAmountData);
availableAmount.setText(availableAmountData);
finishTime.setText(finishTimeData);
}
}
#Override
public void onFailure(Call<Fuel> call, Throwable t) {
Log.e("Failure message",t.getLocalizedMessage());
}
});
}
//Delete fuel details
public void deleteFuel(int id){
Call<Fuel> deleteFuelCall = ApiClient.getFuelServices().deleteFuel(id);
deleteFuelCall.enqueue(new Callback<Fuel>() {
#Override
public void onResponse(Call<Fuel> call, Response<Fuel> response) {
if(response.isSuccessful()){
Toast.makeText(ShedFuelUpdateFormActivity.this, "Fuel deleted successfully", Toast.LENGTH_LONG).show();
Intent intent = getIntent();
String shedId = intent.getStringExtra("shedId");
Intent i = new Intent(ShedFuelUpdateFormActivity.this, ShedOwnerHomeActivity.class).putExtra("shedId",shedId);;
startActivity(i);
}
else{
Toast.makeText(ShedFuelUpdateFormActivity.this, "Failed to delete fuel", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<Fuel> call, Throwable t) {
Log.e("Failure message",t.getLocalizedMessage());
}
});
}
//Update fuel
public Fuel updateFuel(){
Fuel fuel = new Fuel();
Intent intent = getIntent();
int shedId = Integer.parseInt(intent.getStringExtra("shedId"));
fuel.setShedId(shedId);
fuel.setFuelType(fuelType.getText().toString());
fuel.setFuelArivalTime(arrivalTime.getText().toString());
fuel.setArivalAmount(Integer.parseInt(arrivalAmount.getText().toString()));
fuel.setCurrentAvailableAmount(Integer.parseInt(availableAmount.getText().toString()));
fuel.setFuelFinishedTime(finishTime.getText().toString());
return fuel;
}
//Save updated fuel
public void saveFuel (int id, Fuel fuel){
Call<Fuel> createFuelCall = ApiClient.getFuelServices().updateFuel(id, fuel);
createFuelCall.enqueue(new Callback<Fuel>() {
#Override
public void onResponse(Call<Fuel> call, Response<Fuel> response) {
if(response.isSuccessful()){
Toast.makeText(ShedFuelUpdateFormActivity.this, "Fuel details updated successfully", Toast.LENGTH_LONG).show();
Intent intent = getIntent();
String shedId = intent.getStringExtra("shedId");
Intent i = new Intent(ShedFuelUpdateFormActivity.this, ShedOwnerHomeActivity.class).putExtra("shedId",shedId);;
startActivity(i);
}
else{
Toast.makeText(ShedFuelUpdateFormActivity.this, "Failed to update fuel", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<Fuel> call, Throwable t) {
Log.e("Failure message",t.getLocalizedMessage());
}
});
}
}

Closing the app when user cancels immediate in app update

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.

App crashes after clicking scan button for the first time but the app works after i reopen it

I'm trying to create an indoor location services app in android studio.There is a scan button which start the discovery of BLE devices. When i click on the scan button,the app crashes. But when i reopen the app and click on the scan button again,it works.
i tried this taken from one of the projects from stackoverflow.
Class variable:
private BluetoothAdapter mBtAdapter = null;
final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBtAdapter = btManager.getAdapter();
if (mBtAdapter == null || !mBtAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public void onScanButton(){
if (mBtAdapter.isEnabled()){
scanLeDevice(true);
}
}
this is my code
BluetoothManager btManager; //field 'btManager' is never used
private BluetoothAdapter btAdapter = null;
BluetoothLeScanner btScanner;
Button startScanningButton;
Button stopScanningButton;
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
peripheralTextView = (TextView) findViewById(R.id.peripheralTextView);
peripheralTextView.setMovementMethod(new ScrollingMovementMethod());
startScanningButton = (Button) findViewById(R.id.StartScanButton);
startScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startScanning();
}
});
stopScanningButton = (Button) findViewById(R.id.StopScanButton);
stopScanningButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopScanning();
}
});
stopScanningButton.setVisibility(View.INVISIBLE);
final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect peripherals.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
}
// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
peripheralTextView.append("MAC address: " + result.getDevice().getAddress() + " rssi: " + result.getRssi() + "TxPower:" + result.getTxPower() + "\n");
// auto scroll for text view
final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight();
// if there is no need to scroll, scrollAmount will be <=0
if (scrollAmount > 0)
peripheralTextView.scrollTo(0, scrollAmount);
}
};
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("coarse location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
}
}
}
public void startScanning() {
System.out.println("start scanning");
peripheralTextView.setText("");
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.startScan(leScanCallback);
}
});
}
public void stopScanning() {
System.out.println("stopping scanning");
peripheralTextView.append("Stopped Scanning");
startScanningButton.setVisibility(View.VISIBLE);
stopScanningButton.setVisibility(View.INVISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
});
}
}
The logcat shows
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.startScan(android.bluetooth.le.ScanCallback)' on a null object reference
at com.example.myapplication.MainActivity$6.run(MainActivity.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Before i click the Scan button,a prompt will be display asking to turn
on the bluetooth.So bluetooth will be turned on
You are wrong about that part. You ask the user to enable it, but it might not have happened yet. At least you need to get the Scanner later on.
Currently you set the Scanner reference before the permission requesting has been initiated.
This also explains why it works after your App has crashed for the first time, because the 2nd time you come here the Permission has been enabled.
From the Javadoc of BluetoothAdapter#getBluetoothLeScanner():
Will return null if Bluetooth is turned off or if Bluetooth LE
Advertising is not supported on this device.
You can change your code to:
public void startScanning() {
btScanner = btAdapter.getBluetoothLeScanner();
if (btScanner == null) {
// not enabled yet or not supported
return;
}
System.out.println("start scanning");
peripheralTextView.setText("");
startScanningButton.setVisibility(View.INVISIBLE);
stopScanningButton.setVisibility(View.VISIBLE);
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.startScan(leScanCallback);
}
});
}

Android studio app crashes while attempting to send file over bluetooth

Android bluetooth app crashes while trying to send data via bluetooth. I was able to turn on, make discoverable, and show paired devices. But, it crashes when i attempt the send file button. Im getting these errors in my Debugger.
Process: com.example.bluetooth_demoproject, PID: 31320
java.lang.NullPointerException: Attempt to get length of null
array
at com.example.bluetooth_demoproject.MainActivity.ListDir(MainActivity.java:253)
at com.example.bluetooth_demoproject.MainActivity.onPrepareDialog(MainActivity.java:228)
at android.app.Activity.onPrepareDialog(Activity.java:4000)
at android.app.Activity.showDialog(Activity.java:4063)
at android.app.Activity.showDialog(Activity.java:4014)
at
com.example.bluetooth_demoproject.MainActivity$1.onClick(MainActivity.java:75)
at android.view.View.performClick(View.java:6896)
at
android.widget.TextView.performClick(TextView.java:12689)
at android.view.View$PerformClick.run(View.java:26088)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at
android.app.ActivityThread.main(ActivityThread.java:6940)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Also here is my MainActivity. If theres a better way to sending files via
bluetooth please point it out or help with my code.
public class MainActivity extends Activity {
// Creating objects -----------------------------
private static final int REQUEST_ENABLE_BT = 0;
private static final int REQUEST_DISCOVER_BT_ = 1;
private static int CUSTOM_DIALOG_ID;
ListView dialog_ListView;
TextView mBluetoothStatus, mPairedDevicesList, mTextFolder;
ImageView mBluetoothIcon;
Button mOnButton, mOffButton, mDiscoverableButton, mPairedDevices,
mbuttonOpenDialog, msendBluetooth, mbuttonUp;
File root, fileroot, curFolder;
EditText dataPath;
private static final int DISCOVER_DURATION = 300;
private List<String> fileList = new ArrayList<String>();
// -------------------------------------------------------
BluetoothAdapter mBlueAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dataPath =(EditText) findViewById(R.id.FilePath);
mTextFolder = findViewById(R.id.folder);
mBluetoothStatus = findViewById(R.id.BluetoothStatus);
mBluetoothIcon = findViewById(R.id.bluetoothIcon);
mOnButton = findViewById(R.id.onButton);
mOffButton = findViewById(R.id.offButton);
mDiscoverableButton = findViewById(R.id.discoverableButton);
mPairedDevices = findViewById(R.id.pairedDevices);
mPairedDevicesList = findViewById(R.id.pairedDeviceList);
mbuttonOpenDialog = findViewById(R.id.opendailog);
msendBluetooth = findViewById(R.id.sendBluetooth);
mbuttonUp = findViewById(R.id.up);
mbuttonOpenDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dataPath.setText("");
showDialog(CUSTOM_DIALOG_ID);
}
});
root = new
File(Environment.getExternalStorageDirectory().getAbsolutePath());
curFolder = root;
msendBluetooth.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
sendViaBluetooth();
}
});
//adapter
mBlueAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBlueAdapter == null){
mBluetoothStatus.setText("Bluetooth is not available");
return;
}
else {
mBluetoothStatus.setText("Bluetooth is available");
}
//if Bluetooth isnt enabled, enable it
if (!mBlueAdapter.isEnabled()) {
Intent enableBtIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
//set image according to bluetooth Status
if (mBlueAdapter.isEnabled()) {
mBluetoothIcon.setImageResource(R.drawable.action_on);
}
else {
mBluetoothIcon.setImageResource(R.drawable.action_off);
}
//on button Click
mOnButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if (!mBlueAdapter.isEnabled()) {
showToast("Turning Bluetooth on...");
// intent to on bluetooth
Intent intent = new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
}
else {
showToast("Bluetooth is already on");
}
}
});
//discover Bluetooth button
mDiscoverableButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
if (!mBlueAdapter.isDiscovering()) {
showToast("Making device discoverable");
Intent intent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(intent, REQUEST_DISCOVER_BT_);
}
}
});
// off button click
mOffButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mBlueAdapter.isEnabled()) {
showToast("Turning Bluetooth off...");
// intent to turn off bluetooth
mBluetoothIcon.setImageResource(R.drawable.action_off);
}
else{
showToast("Bluetooth is already off");
}
}
});
//get paired device button click
mPairedDevices.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mBlueAdapter.isEnabled()) {
mPairedDevices.setText("Paired Devices");
Set<BluetoothDevice> devices =
mBlueAdapter.getBondedDevices();
for (BluetoothDevice device : devices){
mPairedDevices.append("\nDevice: " + device.getName() +
"," + device );
}
}
else {
//bluetooth is off and cant get paired devices
showToast("Turn on bluetooth to get paired devices");
}
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
Dialog dialog = null;
if (id == CUSTOM_DIALOG_ID) {
dialog = new Dialog(MainActivity.this);
dialog.setContentView(R.layout.dailoglayout);
dialog.setTitle("File Selector");
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
mTextFolder = (TextView) dialog.findViewById(R.id.folder);
mbuttonUp = (Button) dialog.findViewById(R.id.up);
mbuttonUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ListDir(curFolder.getParentFile());
}
});
dialog_ListView = (ListView) dialog.findViewById(R.id.dialoglist);
dialog_ListView.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int
position, long id) {
File selected = new File(fileList.get(position));
if (selected.isDirectory()) {
ListDir(selected);
} else if (selected.isFile()) {
getSelectedFile(selected);
} else {
dismissDialog(CUSTOM_DIALOG_ID);
}
}
});
}
return dialog;
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
if (id == CUSTOM_DIALOG_ID) {
ListDir(curFolder);
}
}
public void getSelectedFile(File f) {
dataPath.setText(f.getAbsolutePath());
fileList.clear();
dismissDialog(CUSTOM_DIALOG_ID);
}
public void ListDir(File f) {
if (f.equals(root)) {
mbuttonUp.setEnabled(false);
}
else {
mbuttonUp.setEnabled(true);
}
curFolder = f;
mTextFolder.setText(f.getAbsolutePath());
dataPath.setText(f.getAbsolutePath());
File[] files = f.listFiles();
fileList.clear();
for (File file : files) {
fileList.add(file.getPath());
}
ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, fileList);
dialog_ListView.setAdapter(directoryList);
}
// exits to app --------------------------------
public void exit(View V) {
mBlueAdapter.disable();
Toast.makeText(this, "*** Now bluetooth is off...",
Toast.LENGTH_LONG).show();
finish();
}
// send file via bluetooth ------------------------
public void sendViaBluetooth() {
if(!dataPath.equals(null)) {
if(mBlueAdapter == null) {
Toast.makeText(this, "Device doesnt support bluetooth",
Toast.LENGTH_LONG).show();
}
else {
enableBluetooth();
}
}
else {
Toast.makeText(this, "please select a file",
Toast.LENGTH_LONG).show();
}
}
public void enableBluetooth() {
showToast("Making device discoverable");
Intent intent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(intent, REQUEST_DISCOVER_BT_);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent
data) {
if (resultCode == DISCOVER_DURATION && requestCode ==
REQUEST_DISCOVER_BT_) {
Intent i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType("*/*");
File file = new File(dataPath.getText().toString());
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(i, 0);
if (list.size() > 0) {
String packageName = null;
String className = null;
boolean found = false;
for (ResolveInfo info : list) {
packageName = info.activityInfo.packageName;
if (packageName.equals("com.android.bluetooth")) {
className = info.activityInfo.name;
found = true;
}
}
//CHECK BLUETOOTH available or not------------------------------
------------------
if (!found) {
Toast.makeText(this, "Bluetooth not been found",
Toast.LENGTH_LONG).show();
} else {
i.setClassName(packageName, className);
startActivity(i);
}
}
} else {
Toast.makeText(this, "Bluetooth is cancelled",
Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
return super.onOptionsItemSelected(item);
}
//toast message function
private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT) .show();
}
}

Update GUI after purchase of an IAP is complete, IAPv3 - Android

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.

Categories

Resources