In my fragment onActivityResult is not calling after startActivityForResult .As i have tried following things:
1) adding getParentFragment , getActivity before startActivityResult
2) added on manifest android:configChanges="orientation|screenSize"
3)onActivityResult is not being called in Fragment
4)Checked finish () method in ZbarScanner Activity
Intent dataIntent = new Intent();
dataIntent.putExtra(SCAN_RESULT, symData);
dataIntent.putExtra(SCAN_RESULT_TYPE, sym.getType());
setResult(Activity.RESULT_OK, dataIntent);
finish();
5) Error- [ResultInfo{who=null, request=196609, result=-1, data=Intent { (has extras) }}]} checked intent has value not null which we are passing
if (isCameraAvailable()) {
Intent intent = new Intent(this.getActivity(), ZBarScannerActivity.class);
intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{Symbol.QRCODE});
startActivityForResult(intent, 1);
Toast.makeText(getActivity(), "inside case", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Rear Facing Camera Unavailable", Toast.LENGTH_SHORT).show();
}
----onActivityResult------
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Bundle bundle = data.getExtras();
switch (requestCode) {
case ZBAR_SCANNER_REQUEST:
case ZBAR_QR_SCANNER_REQUEST:
if (resultCode == getActivity().RESULT_OK) {
Toast.makeText(getActivity(), "Scan Result = " + data.getStringExtra(ZBarConstants.SCAN_RESULT), Toast.LENGTH_SHORT).show();
} else if (resultCode == getActivity().RESULT_CANCELED && data != null) {
String error = data.getStringExtra(ZBarConstants.ERROR_INFO);
if (!TextUtils.isEmpty(error)) {
Toast.makeText(getActivity(), error, Toast.LENGTH_SHORT).show();
}
}}
I called onActivityResult on MainActivity and with this its not calling .so removed switch case .and its working now
switch (requestCode) {
case ZBAR_SCANNER_REQUEST:
case ZBAR_QR_SCANNER_REQUEST:
It is very common and known issue for fragment onActivityResult. I have solved it in one way, there might be other ways also.
set tag when you are moving to Fragment from Activity like this.
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment,"tag").commit();
now write below code in your onActivityResult of Activity.
if (requestCode == 1) { //set your requestcode whichever you are using
//super.onActivityResult(requestCode, resultCode, data);
//dont forget to comment this.
Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("tag");
if(fragment != null){
fragment.onActivityResult(requestCode, resultCode, data);
}
}
I had a similar issue while working with a FragmentPagerAdapter inside a Fragment. The problem was that I initialized my PagerAdapter with the Fragment's FragmentManager, I replaced:
new MyPagerAdapter(getChildFragmentManager());
with
new MyPagerAdapter(getFragmentManager());
So it might be worth checking which FragmentManager you are using. I'm not 100% happy with this approach yet as it seems right to call the Fragment's FragmentManager. But I did not get the time to check it any further yet.
Related
I've created an MFC Application, with a Base Dialog (derived from CDialog class), a Setting Dialog (derived from CBaseDlg and an App Dialog (also derived from CBaseDlg). Then I created a class called CScrMng (aka Screen Manager) that hold the ShowDialog() function (to Create and Show these dialogs).
The basic idea is ScrMng will manage all of my Modeless Dialogs, and anytime I want to open a dialog, I just need to CScrMng::ShowDialog() in the BaseDlg.cpp, and the dialog will display.
This approach has caused resources to leak here and there. I've done a bit of research about overriding the PostNcDestroy(), but I don't have a clear idea of where to call it.
What function should I use to properly close these modeless dialogs?
I want to open the Setting Dialog from Base Dialog, then when I click on the Cancel button, it should return me to the Base Dialog screen so that I can open another Dialog.
Right now I'm using EndDialog(). I know it's wrong, but calling DestroyWindow() will immediately exit the program, which is not what I want.
Source code
MFCApplication.cpp
#include...
BEGIN_MESSAGE_MAP(CMFCApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
CMFCApp::CMFCApp()
{
// support Restart Manager
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
}
CMFCApp theApp;
BOOL CMFCApp::InitInstance()
{
...
CWinApp::InitInstance();
CShellManager *pShellManager = new CShellManager;
CScrMng::GetInstance()->ShowDialog(IDD_MAINDLG);
SetRegistryKey(_T("Local Applications"));
if (pShellManager != NULL)
{
delete pShellManager;
}
return TRUE;
}
CScrMng.cpp
#include...
CScrMng* CScrMng::m_pInstance = NULL;
CScrMng* CScrMng::GetInstance(){
if (m_pInstance == NULL)
m_pInstance = new CScrMng();
return m_pInstance;
}
CScrMng::CScrMng(){}
void CScrMng::ShowDialog(int ID)
{
CMainDlg* m_pDlg = NULL;
switch (ID)
{
case IDD_MAINDLG:
m_pDlg = new CMainDlg();
theApp.m_pMainWnd = m_pDlg;
m_pDlg->Create(IDD_MAINDLG);
m_pDlg->ShowWindow(SW_SHOW);
m_pDlg->UpdateWindow();
break;
case ...
break;
case IDD_SETTINGDLG:
m_pDlg = new CSettingDlg();
m_pDlg->Create(ID,NULL);
m_pDlg->ShowWindow(SW_SHOW);
m_pDlg->UpdateWindow();
break;
}
CMainDlg.cpp
#include...
CMainDlg::CMainDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMainDlg::IDD, pParent) {m_hIcon = AfxGetApp()-> LoadIcon(IDR_MAINFRAME);}
void CMainDlg::DoDataExchange(CDataExchange* pDX) {...}
void CMainDlg::PostNcDestroy() //Added these
{
CDialog::PostNcDestroy();
delete this;
}
BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
...
END_MESSAGE_MAP()
BOOL CMainDlg::OnInitDialog() {
CDialog::OnInitDialog();
SetIcon(m_hIcon, FALSE);
return TRUE;
}
void CMainDlg::OnPaint() {...}
void CMainDlg::OnBnClickedOpenAppdlg()
{
CScrMng::GetInstance()->ShowDialog(IDD_APPDLG);
}
void CMainDlg::OnBnClickedOpenSettingdlg()
{
CScrMng::GetInstance()->ShowDialog(IDD_SETTINGDLG);
}
void CMainDlg::OnBnClickedExit()
{
DestroyWindow(); //replaced CDialog::OnCancel() with this.
}
Update: After changing the code in the SettingDlg.cpp, i encountered a Debug Assertion Failed! problem :
void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)
{
ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL)); //Breakpoint triggered
if (m_pCtrlSite == NULL)
::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint);
else
m_pCtrlSite->MoveWindow(x, y, nWidth, nHeight);
}
Here are what i changed in the .cpp file:
SettingDlg.cpp
void CSettingDlg::PostNcDestroy()
{
CMainDlg::PostNcDestroy();
}
void CSettingDlg::OnBnClickedSettingcancel()
{
DestroyWindow(); //Using destroyWindow rather than EndDialog();
}
Close & Delete a modeless Dialogs.
A proper Way is: Override PostNcDestroy, OnOk() and OnCancel() for the Modeless Dialogs
void CBaseDlg::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this;
}
.
void CBaseDlg::OnOk()
{
if(!UpdateData(TRUE))
return;
DestroyWindow();
}
.
void CBaseDlg::OnCancel()
{
DestroyWindow();
}
I need to run some address validation on Customer Location addresses using a 3rd party API to determine if the address is residential or commercial. This validation should run whenever an address field is changed. In other words, the validation should be run in the Address_RowUpdated event handler.
Because the function is calling a 3rd party API, I believe that it should be done in a separate thread, using PXLongOperation so that it does not hold up address saving and fails gracefully if the API is unavailable or returns an error.
However, I am not sure if the architecture of running a long operation within an event handler is supported or if a different approach would be better.
Here is my code.
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = this.Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
PXLongOperation.StartOperation(Base, delegate
{
RunCheckResidential(location, locationCache);
});
this.Base.LocationCurrent.Cache.IsDirty = true;
}
}
protected void RunCheckResidential(Location location, PXCache locationCache)
{
string messages = "";
PX.Objects.CR.Address defAddress = PXSelect<PX.Objects.CR.Address,
Where<PX.Objects.CR.Address.addressID, Equal<Required<Location.defAddressID>>>>.Select(Base, location.DefAddressID);
FValidator validator = new FValidator();
AddressValidationReply reply = validator.Validate(defAddress);
AddressValidationResult result = reply.AddressResults[0];
bool isResidential = location.CResedential ?? false;
if (result.Classification == FClassificationType.RESIDENTIAL)
{
isResidential = true;
} else if (result.Classification == FClassificationType.BUSINESS)
{
isResidential = false;
} else
{
messages += "Residential classification is: " + result.Classification + "\r\n";
}
location.CResedential = isResidential;
locationCache.Update(location);
Base.LocationCurrent.Update(location);
Base.Actions.PressSave();
// Display relevant messages
if (reply.HighestSeverity == NotificationSeverityType.SUCCESS)
String addressCorrection = validator.AddressCompare(result.EffectiveAddress, defAddress);
if (!string.IsNullOrEmpty(addressCorrection))
messages += addressCorrection;
}
PXSetPropertyException message = new PXSetPropertyException(messages, PXErrorLevel.Warning);
PXLongOperation.SetCustomInfo(new LocationMessageDisplay(message));
//throw new PXOperationCompletedException(messages); // Shows message if you hover over the success checkmark, but you have to hover to see it so not ideal
}
public class LocationMessageDisplay : IPXCustomInfo
{
public void Complete(PXLongRunStatus status, PXGraph graph)
{
if (status == PXLongRunStatus.Completed && graph is CustomerLocationMaint)
{
((CustomerLocationMaint)graph).RowSelected.AddHandler<Location>((sender, e) =>
{
Location location = e.Row as Location;
if (location != null)
{
sender.RaiseExceptionHandling<Location.cResedential>(location, location.CResedential, _message);
}
});
}
}
private PXSetPropertyException _message;
public LocationMessageDisplay(PXSetPropertyException message)
{
_message = message;
}
}
}
UPDATE - New Approach
As suggested, this code now calls the LongOperation within the Persist method.
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = Base.Location.Current;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
locationExt.UsrResidentialValidated = false;
Base.LocationCurrent.Cache.IsDirty = true;
}
}
public delegate void PersistDelegate();
[PXOverride]
public virtual void Persist(PersistDelegate baseMethod)
{
baseMethod();
var location = Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
if (locationExt.UsrResidentialValidated == false)
{
PXLongOperation.StartOperation(Base, delegate
{
CheckResidential(location);
});
}
}
public void CheckResidential(Location location)
{
CustomerLocationMaint graph = PXGraph.CreateInstance<CustomerLocationMaint>();
graph.Clear();
graph.Location.Current = location;
LocationExt locationExt = location.GetExtension<LocationExt>();
locationExt.UsrResidentialValidated = true;
try
{
// Residential code using API (this will change the value of the location.CResedential field)
} catch (Exception e)
{
throw new PXOperationCompletedWithErrorException(e.Message);
}
graph.Location.Update(location);
graph.Persist();
}
PXLongOperation is meant to be used in the context of a PXAction callback. This is typically initiated by a menu item or button control, including built-in actions like Save.
It is an anti-pattern to use it anytime a value changes in the web page. It should be used only when a value is persisted (by Save action) or by another PXAction event handler. You should handle long running validation when user clicks on a button or menu item not when he changes the value.
For example, the built in Validate Address feature is run only when the user clicks on the Validate Address button and if validated requests are required it is also run in a Persist event called in the context of the Save action to cancel saving if validation fails.
This is done to ensure user expectation that a simple change in a form/grid value field doesn't incur a long validation wait time that would lead the user to believe the web page is unresponsive. When the user clicks on Save or a specific Action button it is deemed more reasonable to expect a longer wait time.
That being said, it is not recommended but possible to wrap your PXLongOperation call in a dummy Action and asynchronously click on the invisible Action button to get the long operation running in the proper context from any event handler (except Initialize):
using PX.Data;
using System.Collections;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> TestLongOperation;
[PXUIField(DisplayName = "Test Long Operation", Visible = false, Visibility = PXUIVisibility.Invisible)]
[PXButton]
public virtual IEnumerable testLongOperation(PXAdapter adapter)
{
PXLongOperation.StartOperation(Base, delegate ()
{
System.Threading.Thread.Sleep(2000);
Base.Document.Ask("Operation Done", MessageButtons.OK);
});
return adapter.Get();
}
public void SOOrder_OrderDesc_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
if (!PXLongOperation.Exists(Base.UID))
{
// Calling Action Button asynchronously so it can run in the context of a PXAction callback
Base.Actions["TestLongOperation"].PressButton();
}
}
}
}
I'm trying to get a private Playlist from the user currently logged in into the app.
SpotifyApi api = new SpotifyApi();
api.setAccessToken(response.getAccessToken());
SpotifyService spotify = api.getService();
Playlist playlist = spotify.getPlaylist(user_id, playlist_id);
How can I get user_id?
EDIT
I tried this code:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, data);
if (response.getType() == AuthenticationResponse.Type.TOKEN) {
SpotifyApi api = new SpotifyApi();
api.setAccessToken(response.getAccessToken());
SpotifyService spotify = api.getService();
User user = spotify.getMe();
Log.d("TAG", user.id);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
This gives me an error:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1337, result=-1, data=Intent { (has extras) }} to activity {de.test.spotifytest/de.test.spotifytest.activities.MainActivity}: retrofit.RetrofitError
I had to get the user object in an AsyncTask because it isn't possible to perform network actions on the main thread. The same applies to getting the users playlists.
private class MyTask extends AsyncTask<String, Integer, Pager<Playlist>>{
#Override
protected Pager<Playlist> doInBackground(String... params) {
Pager<Playlist> playlists = spotify.getPlaylists(spotify.getMe().id);
return playlists;
}
#Override
protected void onPostExecute(Pager<Playlist> playlistPager) {
//do something with the playlists
}
}
On main thread:
new MyTask().execute("");
I'm not sure what library you're using, but it looks like the spotify-web-api-android wrapper.
If so, you can retrieve the current user's user ID by calling the Get Current User's Profile endpoint using SpotifyService's getMe() method. getMe() will return a User object that has a member called id.
Update: It seems the issue may not be related to the wrapper, but rather a general Android issue. This Stack Overflow question seems related.
Adding a check to see if the resultCode isn't RESULT_CANCELED before entering the if block may solve this.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_CANCELED && resultCode == REQUEST_CODE) {
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, data);
...
}
}
Others use an additional resultCode == RESULT_OK, which to my understanding is valid as well.
I have a project with 2 activities, the first one is the "SplashActivity" - where I load some network data - the second one, the MainActivity.
Inside of my MainActivity I have a fragment and inside of this fragment a webview. My first point is, when the user clicks on back button, the SplashScreen is open again.
The back button should behave like:
When the user doesn't navigate inside of my webview, close the app.
When the user navigates in webview, use the back history of the browswer.
I read about back stack here: http://developer.android.com/training/implementing-navigation/temporal.html#back-webviews
I didn't understand at all how it should work, because I have all cases "mixed". Anyone knows what should I do to fix this problem?
Any idea or sample code will be appreciate!
Define Webview wb as a global variable. Then try this;
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
switch(keyCode)
{
case KeyEvent.KEYCODE_BACK:
if(wb.canGoBack() == true){
wb.goBack();
}else{
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle("Application will be closed")
.setMessage("Close app?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
System.exit(0);
}
}).setNegativeButton("No", null).show();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
}
I have an activity in which I call a service that can take a while to complete and until that service didn't finish, the menu options that are clicked should return an error message like "data not yet ready, please try again soon"
however, I want to give it a couple of seconds to finish before I throw that error and during that time, I want to show a progressdialog on the screen.
here is the code that I have:
if(calculatedValue.equals(NOT_YET_CALCULATED)){
//show progress dialog
ProgressDialog progress = ProgressDialog.show(this, "", getResources().getString(R.string.wait), true);
long timeStarted = System.currentTimeMillis();
while(calculatedValue.equals(NOT_YET_CALCULATED) && System.currentTimeMillis() - timeStarted < 1500){
// wait for at most 1.5 seconds
}
progress.dismiss();
if(calculatedValue.equals(NOT_YET_CALCULATED)){
//if it's still not there, there's probably a problem, show generic alert dialog
AlertDialog dialog = new AlertDialog.Builder(this).create();
dialog.setTitle(R.string.not_yet_calulated_alert_title);
dialog.setMessage(getResources().getString(R.string.not_yet_calulated_alert_message));
dialog.show();
}else{
startActivity(j);
}
}else{
startActivity(j);
}
Let me explain:
I check if the value exists, and if it doesn't I show a progress icon (i'm going for the circle thingy) for 1.5 seconds. If after that time it's still not there, I give up and show an error message.
The problem is that the progress thing does not show up on the screen.
what am I doing wrong?
thanks,
e.
figured it out, should use async task for this, here's the code:
if(calculatedValue.equals(NOT_YET_CALCULATED)){
//show progress dialog
final ProgressDialog progress = ProgressDialog.show(this, "", getResources().getString(R.string.wait), true);
AsyncTask<Void, Void, Boolean> waitForCompletion = new AsyncTask<Void, Void, Boolean>(){
#Override
protected Boolean doInBackground(Void... params) {
long timeStarted = System.currentTimeMillis();
while(calculatedValue.equals(NOT_YET_CALCULATED) && System.currentTimeMillis() - timeStarted < 1500){
// wait for 1.5 ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Log.e(TAG, "thread interrupted", e);
}
}
progress.dismiss();
return calculatedValue.equals(NOT_YET_CALCULATED);
};
#Override
protected void onPostExecute(Boolean result) {
if(result == true){
AlertDialog dialog = new AlertDialog.Builder(TodayActivity.this).create();
dialog.setTitle(R.string.not_yet_calulated_alert_title);
dialog.setMessage(getResources().getString(R.string.not_yet_calulated_alert_message));
dialog.show();
}else{
i.putExtra(Constants.AMOUNT, Integer.parseInt(calculatedValue));
startActivity(i);
}
}
};
waitForCompletion.execute(null, null, null);
}else{
i.putExtra(Constants.AMOUNT, Integer.parseInt(calculatedValue));
startActivity(i);
}
What you are making now atleast causes a deadlock.
What I normally do is creating the progressdialog as you are doing now and on the same time start another thread that in this situation waits for 1.5second and stop the progressdialog then.
For example
private Runnable waitforcompletion = new Runnable()
{
#Override
public void run()
{
while(calculatedValue.equals(NOT_YET_CALCULATED) && System.currentTimeMillis() - timeStarted < 1500){
Thread.sleep(10);
}
progress.dismiss();
}
};
And for starting just call the above runnable after creating the progressdialog