In my android app, I want to display the localized price, for an In-App purchase, on a button.
I've tried following a guide to set up the In-App Billing (https://medium.com/#patpatchpatrick/adding-the-google-play-billing-library-to-your-application-fbeb9ec03151) to set up the billing itself, and it seems to work on a test account.
Although .setSku and .setType is now deprecated, and .setSkuDetails is now to be used, which from documentation is great, as there's plenty of options. However i can't seem to get any access to the SkuDetails class..
For a couple of weeks I've tried implementing In-App Billing, and looked at various articles and guides, but can't seem to find my way about it. I feel like i've tried everything and doesn't know where to turn next.
public class InAppBilling extends AppCompatActivity implements
PurchasesUpdatedListener {
private static final String TAG = "InAppBilling";
//In APP Produkter
static final String ITEM_SKU_ADREMOVAL = "remove_ads_salary1";
private Button mButton;
private Button back_Button;
private String mAdRemovalPrice;
private SharedPreferences mSharedPreferences;
private BillingClient mBillingClient;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.inappbilling);
mBillingClient = BillingClient.newBuilder(InAppBilling.this).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(int responseCode) {
if (responseCode == BillingClient.BillingResponse.OK){
List skuList = new ArrayList<>();
skuList.add(ITEM_SKU_ADREMOVAL);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
//Processing the response if the code = OK, and skuDetailsList isn't = null(empty)
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null){
for (SkuDetails skuDetails : skuDetailsList){
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
if (ITEM_SKU_ADREMOVAL.equals(sku)){
mAdRemovalPrice = price;
}
}
}
}
});
}
}
#Override
public void onBillingServiceDisconnected() {
// IMPLEMENT RETRY POLICY - TRY TO RESTART ON NEXT REQUEST BY CALLING startConnection()
}
});
mButton = findViewById(R.id.buy_button);
mButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
//THIS skuDetails gives the error 'Cannot resolve symbol
.setSkuDetails(skuDetails)
// .setSku(ITEM_SKU_ADREMOVAL)
// .setType(BillingClient.SkuType.INAPP)
.build();
int responseCode = mBillingClient.launchBillingFlow(InAppBilling.this, flowParams);
}
});
back_Button = findViewById(R.id.back_button);
back_Button.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//Returnere til det fragment man kom fra
InAppBilling.super.onBackPressed();
}
});
}
Well, I'd love to gain access over SkuDetails, so I can use it's method getPrice(), to show localized prices, for my in-app.
Right now, I can't use getPrice().
Make your activity to implement the listeners, then you will be able to access everything more easily.
I reformatted your code to implement all your listeners, and it may have some minor mistakes, but I hope you get the idea.
Notice that I've also implemented the View.OnClickListener which makes the code more clear, yet you will need to assign at module level a variable for the skuDetails being passed to the builder, which I didn't do. Let me know if you have any questions.
public class InAppBilling extends AppCompatActivity implements
PurchasesUpdatedListener, SkuDetailsResponseListener,
BillingClientStateListener, View.OnClickListener
{
private static final String TAG = "InAppBilling";
//In APP Produkter
static final String ITEM_SKU_ADREMOVAL = "remove_ads_salary1";
private Button mButton;
private Button back_Button;
private String mAdRemovalPrice;
private SharedPreferences mSharedPreferences;
private BillingClient mBillingClient;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.inappbilling);
mBillingClient = BillingClient.newBuilder(this).setListener(this).build();
mBillingClient.startConnection(this);
findViewById(R.id.buy_button).setOnClickListener(this);
findViewById(R.id.back_button).setOnClickListener(this);
}
#Override
public void onPurchasesUpdated(int responseCode, #Nullable List<Purchase> purchases)
{
}
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList)
{
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null)
{
for (SkuDetails skuDetails : skuDetailsList)
{
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
if (ITEM_SKU_ADREMOVAL.equals(sku))
{
mAdRemovalPrice = price;
}
}
}
}
#Override
public void onClick(View view)
{
if (view.getId() == R.id.back_button)
{
super.onBackPressed();
}
else if (view.getId() == R.id.buy_button)
{
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
int responseCode = mBillingClient.launchBillingFlow(this, flowParams);
}
}
#Override
public void onBillingSetupFinished(int responseCode)
{
if (responseCode == BillingClient.BillingResponse.OK)
{
List skuList = new ArrayList<>();
skuList.add(ITEM_SKU_ADREMOVAL);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(), this);
}
}
#Override
public void onBillingServiceDisconnected()
{
// IMPLEMENT RETRY POLICY - TRY TO RESTART ON NEXT REQUEST BY CALLING startConnection()
}
}
Related
I've been stuck in a situation and i need some help over here. There are many articles on this topic here but none of them answered my question. I want to implement onBackPressed() in fragments and show dialog box which shows to exit the application or not. Any help would be appreciated.
LoginFragment.java
public class LoginFragment extends Fragment {
public static final String TAG = LoginFragment.class.getSimpleName();
private EditText mEtEmail;
private EditText mEtPassword;
private Button mBtLogin;
private TextView mTvRegister;
private TextView mTvForgotPassword;
private TextInputLayout mTiEmail;
private TextInputLayout mTiPassword;
private ProgressBar mProgressBar;
private CompositeSubscription mSubscriptions;
private SharedPreferences mSharedPreferences;
#NonNull
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_login,container,false);
mSubscriptions = new CompositeSubscription();
initViews(view);
initSharedPreferences();
return view;
}
private void initViews(View v) {
mEtEmail = v.findViewById(R.id.et_email);
mEtPassword = v.findViewById(R.id.et_password);
mBtLogin = v.findViewById(R.id.btn_login);
mTiEmail = v.findViewById(R.id.ti_email);
mTiPassword = v.findViewById(R.id.ti_password);
mProgressBar = v.findViewById(R.id.progress);
mTvRegister = v.findViewById(R.id.tv_register);
mTvForgotPassword = v.findViewById(R.id.tv_forgot_password);
mBtLogin.setOnClickListener(view -> login());
mTvRegister.setOnClickListener(view -> goToRegister());
mTvForgotPassword.setOnClickListener(view -> showDialog());
}
private void initSharedPreferences() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
}
private void login() {
setError();
String email = mEtEmail.getText().toString();
String password = mEtPassword.getText().toString();
int err = 0;
if (!validateEmail(email)) {
err++;
mTiEmail.setError("Email should be valid !");
}
if (!validateFields(password)) {
err++;
mTiPassword.setError("Password should not be empty !");
}
if (err == 0) {
loginProcess(email,password);
mProgressBar.setVisibility(View.VISIBLE);
} else {
showSnackBarMessage("Enter Valid Details !");
}
}
private void setError() {
mTiEmail.setError(null);
mTiPassword.setError(null);
}
private void loginProcess(String email, String password) {
mSubscriptions.add(NetworkUtil.getRetrofit(email, password).login()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse,this::handleError));
}
private void handleResponse(Response response) {
mProgressBar.setVisibility(View.GONE);
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(Constants.TOKEN,response.getToken());
editor.putString(Constants.EMAIL,response.getMessage());
editor.apply();
mEtEmail.setText(null);
mEtPassword.setText(null);
Intent intent = new Intent(getActivity(), HomeActivity.class);
startActivity(intent);
}
private void handleError(Throwable error) {
mProgressBar.setVisibility(View.GONE);
if (error instanceof HttpException) {
Gson gson = new GsonBuilder().create();
try {
String errorBody = ((HttpException) error).response().errorBody().string();
Response response = gson.fromJson(errorBody,Response.class);
showSnackBarMessage(response.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
} else {
showSnackBarMessage("No Internet Connection!");
}
}
private void showSnackBarMessage(String message) {
if (getView() != null) {
Snackbar.make(getView(),message,Snackbar.LENGTH_SHORT).show();
}
}
private void goToRegister(){
FragmentTransaction ft = getFragmentManager().beginTransaction();
RegisterFragment fragment = new RegisterFragment();
ft.replace(R.id.fragmentFrame,fragment,RegisterFragment.TAG);
ft.addToBackStack(null).commit();
}
private void showDialog(){
ResetPasswordDialog fragment = new ResetPasswordDialog();
fragment.show(getFragmentManager(), ResetPasswordDialog.TAG);
}
#Override
public void onDestroy() {
super.onDestroy();
mSubscriptions.unsubscribe();
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements ResetPasswordDialog.Listener {
public static final String TAG = MainActivity.class.getSimpleName();
private LoginFragment mLoginFragment;
private ResetPasswordDialog mResetPasswordDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
loadFragment();
}
}
private void loadFragment() {
if (mLoginFragment == null) {
mLoginFragment = new LoginFragment();
}
getFragmentManager().beginTransaction().replace(R.id.fragmentFrame, mLoginFragment, LoginFragment.TAG).commit();
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String data = intent.getData().getLastPathSegment();
Log.d(TAG, "onNewIntent: " + data);
mResetPasswordDialog = (ResetPasswordDialog) getFragmentManager().findFragmentByTag(ResetPasswordDialog.TAG);
if (mResetPasswordDialog != null)
mResetPasswordDialog.setToken(data);
}
#Override
public void onPasswordReset(String message) {
showSnackBarMessage(message);
}
private void showSnackBarMessage(String message) {
Snackbar.make(findViewById(R.id.activity_main), message, Snackbar.LENGTH_SHORT).show();
}
}
In My Login Fragment, I want to show a dialog box "Do you want to exit the application or not". On Yes it dismiss the current fragment and end the activity otherwise it'll remain active. Help please!
You can even try this way
MainActivity.java
#Override
public void onBackPressed() {
if (getFragmentManager() != null && getFragmentManager().getBackStackEntryCount() >= 1) {
String fragmentTag = getFragmentManager().findFragmentById(R.id.frame_container).getTag();
if(fragmentTag.equals(LoginFragment.getTag())){
// show Dialog code
}else{
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
Add this code in your main activity so that when login fragment is added and you click backpress, then on first if the fragment is added to fragment transaction, then first it finds the fragment and check if its tag is equals to the login fragment tag. Then if both tag matches, then you can show your exit alert dialog.
Android team has prepared a new way of handling the back button pressed on Fragments for us, so you should check this out. It's called OnBackPressedDispatcher.
You need to register OnBackPressedCallback to the fragment where do you want to intercept back button pressed. You can do it like this inside of the Fragment:
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
OnBackPressedCallback callback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
//show exit dialog
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}
I've been trying to program a working shop system for days, but I see that the Billing Client is not connected.
Toast message here is error connecting to billing.
Where is the mistake?
Do I need a help class? In many instructions it is said that you can use a help classe but do not have to.
With which possibility one can execute to each article another code if the Aktikel was bought?
I would like to offer six digital products that the user can buy.
thank you for your help.
Here is my ShopActivity.class code:
public class ShopActivity extends AppCompatActivity implements PurchasesUpdatedListener {
private BillingClient billingClient;
Button loadProdukt;
RecyclerView recyclerProdukt;
// IabHelper mHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shop);
// String base64EncodedPublicKey = "_BASE64_KEY_";
// mHelper = new IabHelper(this, base64EncodedPublicKey);
// TODO 01.07.2019 IabHelper?
setupBillingClient();
loadProdukt = findViewById(R.id.btn_load_produkt);
recyclerProdukt = findViewById(R.id.recycler_view_paket);
recyclerProdukt.setHasFixedSize(true);
recyclerProdukt.setLayoutManager(new LinearLayoutManager(this));
// event
loadProdukt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(billingClient.isReady()){
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(Arrays.asList("10_coins","20_coins","30_coins","40_coins","80_coins","200_coins"))
.setType(BillingClient.SkuType.INAPP)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() == BilllingResonse.OK) {
loadProduktToRecyclerView(skuDetailsList);
}else{
Toast.makeText(ShopActivity.this, "Cannot query product", Toast.LENGTH_SHORT).show();
}
}
});
}else{
Toast.makeText(ShopActivity.this, "Billing not ready", Toast.LENGTH_SHORT).show();
}
}
});
}
private void loadProduktToRecyclerView(List<SkuDetails> skuDetailsList) {
ProduktAdapter adapter = new ProduktAdapter(this, skuDetailsList,billingClient);
recyclerProdukt.setAdapter(adapter);
}
private void setupBillingClient() {
// billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient = BillingClient.newBuilder(this).setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BilllingResonse.OK) {
Toast.makeText(ShopActivity.this, "Success to connect Billing", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(ShopActivity.this, "Error not connect to Billing", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBillingServiceDisconnected() {
Toast.makeText(ShopActivity.this, "You are disconnect.", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
// if user click Buy, we will retrieve data here
Toast.makeText(this, "Purchase item: "+purchases.size(), Toast.LENGTH_SHORT).show();
if (billingResult.getResponseCode() == BillingResponse.OK && purchases != null) {
for ( Purchase purchases : purchases) {
handlePurchase (purchases);
}
}else if (billingResult.getResponseCode() == BillingResponse.USER_CANCELED) {
}else{
}
}
}
AIDL is deprecated use Google Play Billing Library
I am trying to use realm database to display my api data. I want to display the company name, however the data is saids it is inserted in the log but cant seem to display the data on the UI. Here is the code..
Any help would be greatly appreciated with this problem. The variables are at the top and the problem is when it hits on success, ive written the code "write to DB", but it doesnt display the data but tells me the data has been inserted.
// Variables for the search input field and results TextViews.
private EditText mCompanyInput;
private TextView mTitleText;
private TextView mDescriptionText;
private TextView mOfficerText;
private TextView mTitleText1;
private TextView mDescriptionText1;
private OkHttpClient okHttpClient;
private static final String TAG = "MainActivity";
private Request request;
private String url = "https://api.companieshouse.gov.uk/search/companies?q=";
Button save;
TextView log;
Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCompanyInput = findViewById(R.id.companyInput);
log = findViewById(R.id.log);
mDescriptionText = findViewById(R.id.descriptionText);
mOfficerText = findViewById(R.id.officerText);
mTitleText1 = findViewById(R.id.titleText1);
mTitleText = findViewById(R.id.titleText);
mDescriptionText1 = findViewById(R.id.descriptionText1);
save = findViewById(R.id.searchButton);
realm = Realm.getDefaultInstance();
save.setOnClickListener(this);
}
public void onClick(View view){
okHttpClient = new OkHttpClient();
request = new Request.Builder().url(url).header("Authorization", "k6DNRbTp-AnQWn51JBz5VuPiTl8jv4_etdzoMyhf") .method("GET", null).build();
Log.d(TAG, "onClick:"+url);
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.body().string());
Log.d(TAG, "onResponse:"+response.code());
}
});
writeToDB(mCompanyInput.getText().toString().trim(), (mDescriptionText.getText().toString().trim()));
showData();
}
public void showData(){
RealmResults<Company> guests = realm.where(Company.class).findAll();
// Use an iterator to invite all guests
String op="";
for (Company guest : guests) {
op+=guest.getName();
op+=guest.getAppointments();
}
log.setText(op);
}
public void writeToDB(final String mTitleText1, final String mDescriptionText1){
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Company user = new Company(mTitleText1, mDescriptionText1);
bgRealm.insert(user);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
writeToDB(mCompanyInput.getText().toString().trim(), (mOfficerText.getText().toString().trim()));
showData();
// Transaction was a success.
Log.v("Database", "Data Inserted");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.e("Database", error.getMessage());
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
Why are you calling writeToDB() from the onSuccess() method? This will cause recursion and keep writing the same data into the realm. It's correct to call showData() from onSuccess(), but there's not much point calling it directly from onClick().
I think your problem though is that you're trying to update the UI from a thread: it's called from an async transaction thread and not the main thread. See this answer (and there are others you can find easily once you know the problem: Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread.
I am using this code to save key-value pair in shared preferences and its working fine on my device but on emulators and other real devices, it always returns the default values.
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public static final String USER_PREFS = "com.aamir.friendlocator.friendlocator.USER_PREFERENCE_FILE_KEY";
SharedPreferences sharedPreferences;
private static String userKey="";
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
static final int PERMISSION_ACCESS_FINE_LOCATION = 1;
boolean FINE_LOCATION_PERMISSION_GRANTED = false;
TextView textViewLocationData;
TextView textViewKeyDisplay;
Button buttonRefresh;
Button btnCopyKey;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
goToActivityFriends();
}
});
fab.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_people_white_48dp));
textViewLocationData = (TextView) findViewById(R.id.textViewLocationData);
textViewKeyDisplay =(TextView) findViewById(R.id.tvKeyDisplay);
buttonRefresh = (Button) findViewById(R.id.buttonRefresh);
btnCopyKey = (Button) findViewById(R.id.btnCopyKey);
sharedPreferences = getApplicationContext().getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE);
String key = sharedPreferences.getString("key", "");
if(!key.equals("")) {
textViewKeyDisplay.setText(key);
}
// Create an instance of GoogleAPIClient.
buildGoogleApiClient();
//user_sp = getSharedPreferences(USER_PREFS, 0);
buttonRefresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
displayLocation();
}
});
btnCopyKey.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("userKey", textViewKeyDisplay.getText().toString());
clipboard.setPrimaryClip(clip);
Toast.makeText(getBaseContext(), "Key copied !", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
protected void onStart() {
super.onStart();
if (mGoogleApiClient != null) mGoogleApiClient.connect();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
}
private void displayLocation() {
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if ( permissionCheck != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},PERMISSION_ACCESS_FINE_LOCATION);
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
double latitude = mLastLocation.getLatitude();
double longitude = mLastLocation.getLongitude();
textViewLocationData.setText(latitude + ", " + longitude);
sharedPreferences = getApplicationContext().getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE);
String key = sharedPreferences.getString("key", "");
Log.d("User Key",key);
updateServers(latitude, longitude,key);
} else {
textViewLocationData
.setText("Couldn't get the location. Make sure location is enabled on the device");
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_ACCESS_FINE_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
FINE_LOCATION_PERMISSION_GRANTED = true;
//displayLocation();
} else {
FINE_LOCATION_PERMISSION_GRANTED = false;
}
return;
}
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i("", "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
#Override
public void onConnected(Bundle arg0) {
// Once connected with google api, get the location
//displayLocation();
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
}
public void goToActivityFriends () {
Intent intent = new Intent(this, com.aamir.friendlocator.friendlocator.Friends.class);
startActivity(intent);
}
public void updateServers(Double lat,Double lon,String Key) {
if (Key.equals("")) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
SendLocation cleint = retrofit.create(SendLocation.class);
Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call = cleint.registerUser(String.valueOf(lat), String.valueOf(lon), Key);
call.enqueue(new Callback<com.aamir.friendlocator.friendlocator.Models.SendLocation>() {
#Override
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getUserKey());
if (!response.body().getUserKey().isEmpty()) {
String key_user = response.body().getUserKey();
textViewKeyDisplay.setText(key_user);
// Writing data to SharedPreferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", userKey);
if(editor.commit()){
Log.d("saved","saved");
}
}
}
#Override
public void onFailure(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Throwable t) {
Log.e("Response", t.toString());
}
});
}
else {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("http://demoanalysis.com/pro03/FriendLocator/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
SendLocation cleint = retrofit.create(SendLocation.class);
Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call = cleint.updateLocation(String.valueOf(lat), String.valueOf(lon), Key);
call.enqueue(new Callback<com.aamir.friendlocator.friendlocator.Models.SendLocation>() {
#Override
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getLocationStatus());
if (!response.body().getLocationStatus().isEmpty()) {
Toast.makeText(MainActivity.this,response.body().getLocationStatus(),Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Throwable t) {
Log.e("Response", t.toString());
}
});
}
}
}
On some devices, it's working perfectly. I did change context from this to getApplicationContext but no progress. I have updated the code.
Edit:
tl;dr : you write the wrong variable into the preferences.
Your variable userKey is never written and always an empty string.
In your retrofit onResponse you put userKey as value of "key" into the
preferences. This writes an empty string into the preferences. This will work and give you no error.
Please assign userKey with the value of key_user.
Your response is only stored to key_user.
Or directly remove the local variable key_user as follows:
public void onResponse(Call<com.aamir.friendlocator.friendlocator.Models.SendLocation> call, Response<com.aamir.friendlocator.friendlocator.Models.SendLocation> response) {
Log.d("Response", response.body().getUserKey());
if (!response.body().getUserKey().isEmpty()) {
String userKey = response.body().getUserKey();
textViewKeyDisplay.setText(userKey);
// Writing data to SharedPreferences
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", userKey);
if(editor.commit()){
Log.d("saved","saved");
}
}
}
Before:
In your code to save, you directly try to gather the previously saved value using editor.apply();
As documentation states out, apply will save your changes in background on a different thread.
Therefore your changes might not be saved at the time you try to get the value,
some lines below.
Try to use editor.commit(); instead and check if the problem is still there.
I'm share here my own Preference Class it's too easy so you can put in any project.
Put this class into your util folder or anywhere.
AppPreference.java
package util;
import android.content.Context;
import android.content.SharedPreferences;
/**
* Created by Pranav on 25/06/16.
*/
public class AppPreference {
public static final String PREF_IS_LOGIN = "prefIsLogin";
public static final class PREF_KEY {
public static final String LOGIN_STATUS = "loginstatus";
}
public static final void setStringPref(Context context, String prefKey, String key, String value) {
SharedPreferences sp = context.getSharedPreferences(prefKey, 0);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
public static final String getStringPref(Context context, String prefName, String key) {
SharedPreferences sp = context.getSharedPreferences(prefName, 0);
return sp.getString(key, "");
}
}
Set Preference Value in Login.java when user Login set value like this :
AppPreference.setStringPref(context, AppPreference.PREF_IS_LOGIN, AppPreference.PREF_KEY.LOGIN_STATUS, "0");
Then you will get Login Status Value in any Class by Calling like this :
String LoginStatus = AppPreference.getStringPref(context, AppPreference.PREF_IS_LOGIN, AppPreference.PREF_KEY.LOGIN_STATUS);
Basically this it the code structure, I would like to know how i can modify my codes so that I can get the value inside onResponse and returning it. As of now, my mainReply variable return "(blank)" but im expecting it to pass the data in the arraylist called details inside my onResponse segment. Rest assure, there are values returned as I have checked, but i just cant get the value to be passed out of the onResponse segment.
I have checked for alternatives and they mentioned to use interface. However, I do not know how to modify my codes to use the solution that mentioned interface and use of callBacks.
public class MainActivity extends AppCompatActivity {
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails().execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
#Override
public String doInBackground(List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
If you wanted to modify your existing code, you would add an interface like the one I added up top (RevealDetailsCallbacks), pass it into the asynctask constructor, and run it. The code would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails(callback).execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
private RevealDetailsCallbacks listener;
retrieveDetails(RevealDetailsCallbacks listener){
this.listener = listener;
}
#Override
public String doInBackground(final List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
if(listener != null) {
listener.getDataFromResult(details);
}
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
//Don't make a toast here, it will throw an exception due to it being in doInBackground
//Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
However, there is no need for asynctask here since you are running Retrofit and calling .enqueue, which runs on a background thread. A simpler version would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
//Keep your same variables here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Same setup here
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Same setup here, then call the method
makeWebCalls();
}
});
}
private void makeWebCalls(){
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
//Run your response code here. When done, pass to the callback
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
You can just enqueue the Retrofit call immediately in the OnClick and handle its response there
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String text = et_message.getText().toString();
// if you're trying to filter data, add a parameter to getPatients()
api.getPatients().enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
// Here you have a full list of patients
final List<Patient> patients = response.body();
// adapter = new PatientAdapter(MainActivity.this, patients);
// mListView.setAdapter(adapter);
}