friends,
I am working on Kotlin and trying to fetch the received SMS in the edit text.I don't know how to implement it. If anybody has the correct Kotlin code to fetch OTP please post it below however I have just implemented as below and stacked in the implementation so please post below Kotlin code if anybody has it
class SmsReceiver: BroadcastReceiver() {
private val mListener: SmsListener? = null
var b: Boolean? = null
var abcd: String? = null
var xyz:String? = null
#Suppress("DEPRECATION")
override fun onReceive(context: Context?, intent: Intent?) {
//val bundle = intent!!.getExtras()
val data = intent!!.extras
val pdus = data.get("pdus") as Array<Any>
}
}
below is the activity
public class OtpActivity extends Activity
{
EditText ed;
TextView tv;
String otp_generated,contactNo,id1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.otp);
ed=(EditText)findViewById(R.id.otp_et);
tv=(TextView) findViewById(R.id.verify_otp);
SmsReceiver.bin(new SmsListener() {
#Override
public void messageReceived(String messageText) {
ed.setText(messageText);
}
});
}
}
This can be done with RxJava:
public static Observable<String> createMessageMonitor(#NonNull final Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
return fromIntentFilter(context, intentFilter)
.filter(intent -> intent != null && intent.getExtras() != null)
.map(intent -> {
Bundle bundle = intent.getExtras();
Object[] pdu_Objects = (Object[]) bundle.get("pdus");
if (pdu_Objects != null) {
for (Object aObject : pdu_Objects) {
final SmsMessage currentSMS = getIncomingMessage(aObject, bundle);
final String senderNumber = currentSMS.getDisplayOriginatingAddress();
final String message = currentSMS.getDisplayMessageBody().trim();
Matcher matcher = OTP_PATTERN.matcher(message);
if (matcher.matches()) {
return matcher.group(1);
}
}
}
return "";
}).filter(message -> !message.isEmpty());
}
Your OTP_PATTERN:
private static Pattern OTP_PATTERN = Pattern.compile("^(\\d{4}) is the OTP for your App.$");
Then subscribe the stream:
RxBroadcastReceivers2.createMessageMonitor(this)
.subscribeWith(this,
onNext = { /*Your OTP is here*/ },
onError = {
Timber.e(it, "Registration permission request failed")
}
)
NOTE: You need to get Manifest.permission.READ_SMS before using createMessageMonitor method.
If you don't want to write a receiver, you can use a simple lightweight library https://github.com/VitaliBov/SmsInterceptor
The library has 100% compatibility with Kotlin
You only need to override the interface method, create an Interceptor class and bind it with the life cycle. It looks like this:
public class AuthActivity extends AppCompatActivity implements OnMessageListener {
private SmsInterceptor smsInterceptor;
private EditText etAuthPassword;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth);
initViews();
initSmsInterceptor();
}
#Override
protected void onResume() {
super.onResume();
smsInterceptor.register();
}
#Override
protected void onPause() {
super.onPause();
smsInterceptor.unregister();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
smsInterceptor.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
#Override
public void messageReceived(String message) {
// You can perform your validation here
etAuthPassword.setText(message);
}
private void initViews() {
etAuthPassword = findViewById(R.id.etAuthPassword);
etAuthPassword.addTextChangedListener(new SmsTextWatcher() {
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.length() == 4) {
btnAuthPassword.setEnabled(true);
checkCode();
} else {
btnAuthPassword.setEnabled(false);
}
}
});
}
private void initSmsInterceptor() {
smsInterceptor = new SmsInterceptor(this, this);
// Not necessary
smsInterceptor.setRegex(SMS_CODE_REGEX);
smsInterceptor.setPhoneNumber(PHONE_NUMBER);
}
private void checkCode() {
// Validation
if (isValid) {
navigateToMainScreen();
}
}
}
Related
I have a list of different mines. Each mine has a list of blocks.
I have the mines in a spinner and the blocks in a recyclerview.
I want to display the different lists of blocks whenever the user changes the mine in the mine spinner
I am using Firebase in the backend as my database.
When I change the mine in the spinner, I update the block list by creating a new MutableLiveData which I've extended in a class called FirebaseQueryLiveData
The first time that I initialise the FirebaseQueryLiveData with the quesry containing the mine name, all the events inside it fire. However, after that, I call it and nothing fires. It breaks in the constructor if I have a breakpoint there, but it never reaches the run() method, onActive() method or the onDataChanged in the ValueEventListener.
I have done some research, and I have seen suggestions to replace the LiveData with MutableLiveData. I've done this, and it doesn't seem to make a difference.
Can anyone see anything in the code which I might be missing? I'm not very familiar with the android architecture components and I got the FirebaseQueryLiveData class from another helpful website with a tutorial, so I'm battling to understand where I have gone wrong.
I have done some research, and I have seen suggestions to replace the LiveData with MutableLiveData. I've done this, and it doesn't seem to make a difference.
public class BlockListActivityViewModel extends ViewModel {
private static DatabaseReference blockOutlineRef; // = FirebaseDatabase.getInstance().getReference(FireBasePaths.BLOCKOUTLINE.getPath("Therisa"));
private static DatabaseReference mineListRef;
private FirebaseQueryLiveData blockOutlineLiveDataQuery = null;
private LiveData<BlockOutlineList> blockOutlineLiveData = null;
private MediatorLiveData<String> selectedBlockNameMutableLiveData;
private MediatorLiveData<ArrayList<String>> mineListMutableLiveData;
public BlockListActivityViewModel() {
User loggedInUser = UserSingleton.getInstance();
setUpFirebasePersistance();
setupMineLiveData(loggedInUser);
// setupBlockOutlineListLiveData();
}
private void setupBlockOutlineListLiveData(String mineName) {
if (mineName != "") {
blockOutlineRef = FirebaseDatabase.getInstance().getReference(FireBasePaths.BLOCKOUTLINE.getPath(mineName));
blockOutlineLiveDataQuery = new FirebaseQueryLiveData(blockOutlineRef);
blockOutlineLiveData = Transformations.map(blockOutlineLiveDataQuery, new BlockOutlineHashMapDeserialiser());
}
}
private void setupMineLiveData(User user) {
ArrayList<String> mineNames = new ArrayList<>();
if (user != null) {
if (user.getWriteMines() != null) {
for (String mineName : user.getWriteMines().values()) {
mineNames.add(mineName);
}
}
}
setMineListMutableLiveData(mineNames);
if (mineNames.size() > 0) {
updateMineLiveData(mineNames.get(0));
}
}
public void updateMineLiveData(String mineName) {
SelectedMineSingleton.setMineName(mineName);
setupBlockOutlineListLiveData(SelectedMineSingleton.getInstance());
}
public void setUpFirebasePersistance() {
int i = 0;
// FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
private MutableLiveData<NamedBlockOutline> selectedBlockOutlineMutableLiveData;
public MutableLiveData<NamedBlockOutline> getSelectedBlockOutlineMutableLiveData() {
if (selectedBlockOutlineMutableLiveData == null) {
selectedBlockOutlineMutableLiveData = new MutableLiveData<>();
}
return selectedBlockOutlineMutableLiveData;
}
public void setSelectedBlockOutlineMutableLiveData(NamedBlockOutline namedBlockOutline) {
getSelectedBlockOutlineMutableLiveData().postValue(namedBlockOutline);
}
public MediatorLiveData<String> getSelectedBlockNameMutableLiveData() {
if (selectedBlockNameMutableLiveData == null)
selectedBlockNameMutableLiveData = new MediatorLiveData<>();
return selectedBlockNameMutableLiveData;
}
public void setSelectedBlockNameMutableLiveData(String blockName) {
selectedBlockNameMutableLiveData.postValue(blockName);
}
public MediatorLiveData<ArrayList<String>> getMineListMutableLiveData() {
if (mineListMutableLiveData == null)
mineListMutableLiveData = new MediatorLiveData<>();
return mineListMutableLiveData;
}
public void setMineListMutableLiveData(ArrayList<String> mineListString) {
getMineListMutableLiveData().postValue(mineListString);
}
private class BlockOutlineHashMapDeserialiser implements Function<DataSnapshot, BlockOutlineList>, android.arch.core.util.Function<DataSnapshot, BlockOutlineList> {
#Override
public BlockOutlineList apply(DataSnapshot dataSnapshot) {
BlockOutlineList blockOutlineList = new BlockOutlineList();
HashMap<String, NamedBlockOutline> blockOutlineStringHashMap = new HashMap<>();
for (DataSnapshot childData : dataSnapshot.getChildren()) {
NamedBlockOutline thisNamedOutline = new NamedBlockOutline();
HashMap<String, Object> blockOutlinePointHeader = (HashMap<String, Object>) childData.getValue();
HashMap<String, BlockPoint> blockOutlinePoints = (HashMap<String, BlockPoint>) blockOutlinePointHeader.get("blockOutlinePoints");
thisNamedOutline.setBlockName(childData.getKey());
thisNamedOutline.setBlockOutlinePoints(blockOutlinePoints);
blockOutlineStringHashMap.put(childData.getKey(), thisNamedOutline);
}
blockOutlineList.setBlockOutlineHashMap(blockOutlineStringHashMap);
return blockOutlineList;
}
}
#NonNull
public LiveData<BlockOutlineList> getBlockOutlineLiveData() {
return blockOutlineLiveData;
}
}
LiveData
public class FirebaseQueryLiveData extends MutableLiveData<DataSnapshot> {
private static final String LOG_TAG = "FirebaseQueryLiveData";
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
private boolean listenerRemovePending = false;
private final Handler handler = new Handler();
public FirebaseQueryLiveData(Query query) {
this.query = query;
}
public FirebaseQueryLiveData(DatabaseReference ref) {
this.query = ref;
}
private final Runnable removeListener = new Runnable() {
#Override
public void run() {
query.removeEventListener(listener);
listenerRemovePending = false;
Log.d(LOG_TAG, "run");
}
};
#Override
protected void onActive() {
super.onActive();
if (listenerRemovePending) {
handler.removeCallbacks(removeListener);
Log.d(LOG_TAG, "listenerRemovePending");
}
else {
query.addValueEventListener(listener);
Log.d(LOG_TAG, "addValueEventListener");
}
listenerRemovePending = false;
Log.d(LOG_TAG, "listenerRemovePending");
}
#Override
protected void onInactive() {
super.onInactive();
// Listener removal is schedule on a two second delay
handler.postDelayed(removeListener, 4000);
listenerRemovePending = true;
Log.d(LOG_TAG, "listenerRemovePending");
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
I am using android.telecom.InCallService in one of my of projects. It gives all telephony states excellently, but once you implement this service you need to create your own dialler, which can handle all call features i.e. call merge, conferencing, call waiting, etc.
Any link or suggestion to implement call waiting, merge and conference would be great help.
I've only found how to hold a call, in this repository:
CustomPhoneDialer. See the InCallService, on the method onCallAdded (Override), create a new OngoingCall.
onCallAdded(Call call){
....
new OngoingCall().setCall(call)
....
}
In OngoingCall class you need to create methods that you go to use in your activity like answer, reject, etc.. And one of you need, hold/unhold:
public class OngoingCall {
public static BehaviorSubject<Integer> state = BehaviorSubject.create();
private static Call call;
private Object callback = new Call.Callback() {
#Override
public void onStateChanged(Call call, int newState) {
super.onStateChanged(call, newState);
state.onNext(newState);
}
};
public final void setCall(#Nullable Call value) {
if (call != null) {
call.unregisterCallback((Call.Callback)callback);
}
if (value != null) {
value.registerCallback((Call.Callback)callback);
state.onNext(value.getState());
}
call = value;
}
public void answer() {
assert call != null;
call.answer(VideoProfile.STATE_AUDIO_ONLY);
}
public void hangup() {
assert call != null;
call.disconnect();
}
public void hold(){
assert call != null;
call.hold();
}
public void unHold(){
assert call != null;
call.unhold();
}
....
}
And in the activity, in a button listener use these methods:
private OngoingCall ongoingCall;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call);
ButterKnife.bind(this);
ongoingCall = new OngoingCall();
disposables = new CompositeDisposable();
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
number = Objects.requireNonNull(getIntent().getData()).getSchemeSpecificPart();
}
#OnClick(R.id.answer)
public void onAnswerClicked() {
ongoingCall.answer();
}
#OnClick(R.id.hangup)
public void onHangupClicked() {
ongoingCall.hangup();
}
#OnClick(R.id.hold)
public void onHoldClicked(){
ongoingCall.hold();
}
#OnClick(R.id.unhold)
public void onUnHoldClicked(){
ongoingCall.unHold();
}
}
Updated :
I have build a image cropping app its running fine, but now I want to save cropped image name as textbox value.
In short I am trying to set textbox value in object and get object value in java Class. I have tried several techniques, recently I am trying to get,set data by using interface technique and the image is saved as ".jpg"only.
I would love to know where am I going wronk?
Following is the code I have tried.
MainActivity
public class TestActivity extends AppCompatActivity implements CropHandler, View.OnClickListener {
public static final String TAG = "TestActivity";
ImageView mImageView;
EditText formnumber;
String formid;
CropParams mCropParams;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
mCropParams = new CropParams(this);
mImageView = (ImageView) findViewById(R.id.image);
formnumber =(EditText)findViewById(R.id.FormNumber);
findViewById(R.id.bt_crop_capture).setOnClickListener(this);
findViewById(R.id.bt_crop_gallery).setOnClickListener(this);
}
#Override
public void onClick(View v) {
mCropParams.refreshUri();
formid=formnumber.getText().toString();
// Intent i = new Intent(TestActivity.this, CropHelper.class);
// i.putExtra("Id",formid);
if(formid.matches(""))
{
Toast.makeText(getApplicationContext(),"Please Enter Application Id",Toast.LENGTH_SHORT).show();
}
else
{
switch (v.getId()) {
case R.id.bt_crop_capture: {
mCropParams.enable = true;
mCropParams.compress = false;
Intent intent = CropHelper.buildCameraIntent(mCropParams);
startActivityForResult(intent, CropHelper.REQUEST_CAMERA);
}
break;
case R.id.bt_crop_gallery: {
mCropParams.enable = true;
mCropParams.compress = false;
Intent intent = CropHelper.buildGalleryIntent(mCropParams);
startActivityForResult(intent, CropHelper.REQUEST_CROP);
}
break;
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
CropHelper.handleResult(this, requestCode, resultCode, data);
if (requestCode == 1) {
Log.e(TAG, "");
}
}
#Override
public void onTaskComplete(String response) {
onTaskComplete(this.formid);
}
}
CropHelper Class
public class CropHelper {
public static final String TAG = "CropHelper";
/**
* request code of Activities or Fragments
* You will have to change the values of the request codes below if they conflict with your own.
*/
public static final int REQUEST_CROP = 127;
public static final int REQUEST_CAMERA = 128;
public static final int REQUEST_PICK = 129;
public static String AppId;
public static final String CROP_CACHE_FOLDER = "PhotoCropper";
public static Uri generateUri() {
File cacheFolder = new File(Environment.getExternalStorageDirectory() + File.separator + CROP_CACHE_FOLDER);
if (!cacheFolder.exists()) {
try {
boolean result = cacheFolder.mkdir();
Log.d(TAG, "generateUri " + cacheFolder + " result: " + (result ? "succeeded" : "failed"));
} catch (Exception e) {
Log.e(TAG, "generateUri failed: " + cacheFolder, e);
}
}
// String name = String.format("image-%d.jpg", System.currentTimeMillis());
String name = String.format(AppId.toString()+".jpg",System.currentTimeMillis());
return Uri
.fromFile(cacheFolder)
.buildUpon()
.appendPath(name)
.build();
}
#Override
public void onTaskComplete(String response) {
AppId=response;
}
}
Interface
public interface CropHandler
{
void onPhotoCropped(Uri uri);
void onCompressed(Uri uri);
void onTaskComplete(String response);
void onCancel();
void onFailed(String message);
void handleIntent(Intent intent, int requestCode);
CropParams getCropParams();
}
Set formid to EditText value and get the return value in your CropHelper class.
public static String formid=null;
formid=formnumber.getText().toString();
Now create an object of your Activity in a class where you want to call formid value.
MainActivity my_objec= new MainActivity();
String id= my_objec.formid;
String name = String.format(""+id+".jpg",System.currentTimeMillis());
thats all you need to do.
Implement this with your class and get return back your value in interface
public interface onTaskComplete {
void onComplete(String response);
}
Normally what i do is create different class which holds/save all data and values which can used across differnt classes in app.
For example:
// your activity
private CropHelper cropHelper;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CropHelper.REQUEST_CROP) {
cropHelper.onReceivedImageData(data.get...)
}
}
public interface DataCallBack {
public void OnReceivedImageData(Data data);
}
....
// your crop helper
public class CropHelper implements YourActivity.DataCallBack {
#Override
public void OnReceivedImageData(Data data) {
// doing anything with data
}
}
Best Approach for this is using interface try to do as :
Create Interface
public interface MyListener {
// you can define any parameter as per your requirement
public void callback(View view, String result);
}
public class MyActivity extends Activity implements MyListener {
#override
public void onCreate(){
MyButton m = new MyButton(this);
}
// method invoke when mybutton will click
#override
public void callback(View view, String result) {
// do your stuff here
}
}
public class MyButton {
MyListener ml;
// constructor
MyButton(MyListener ml) {
this.ml = ml;
}
public void MyLogicToIntimateOthere() {
ml.callback(this, "success");
}
}
for more Go to this link:
Using Interface
Pass data through arguments in constructor..,
For example.. Create Constructor in your class.
public class CropHelper {
private Context context;
private String msg;
public CropHelper(Context context, String msg) {
this.context = context;
this.msg = msg;
if (msg != null) {
showMsg(msg);
}
}
//Replace with your logic
void showMsg(String msg) {
//Perform your operation
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
And then simple call it from any Activity by Creating instance of that class..
Like..
new CropHelper(this, "Hello from Activity");
I have an Activity1 with a TabLayout of two Fragments (each of them with a presenter). Once I click a button on the Toolbar a new Activity2 is started (with startActivityWithResults) which contains a simple list. At the selection of one of the items in the list the Activity2 returns the selected string to the previous Activity1 (the one with the TabLayout).
Now, once onActivityResult is called in Activity1, this one will call an API (using a presenter) that will get the new results and then it should update the two fragments in the TabLayout. I'm thinking to do it with RxJava but I have no idea where to start from.
The Activity1:
public class Activity1 extends BaseActivity {
#Inject
Actvity1Presenter mPresenter;
public static Intent newIntent(Context packageContext) {
return new Intent(packageContext, Activity1.class);
}
#LayoutRes
protected int getLayoutRedIs() {
return R.layout.app_bar_activity1;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutRedIs());
FwApplication.component(this).inject(this);
mPresenter.attachView(this);
Toolbar tb = (Toolbar) findViewById(R.id.toolbar_chips);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_back_arrow);
mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mTabLayout.addTab(mTabLayout.newTab().setText("TAB1"));
mTabLayout.addTab(mTabLayout.newTab().setText("TAB2"));
mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(),
mTabLayout.getTabCount()));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_ITEM_CODE) {
if (resultCode == RESULT_OK) {
mPresenter.updateResults(data);
}
if (resultCode == RESULT_CANCELED) {
}
}
}
And the pager:
public class PagerAdapter extends FragmentPagerAdapter {
int mNumOfTabs;
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return Fragment1.newInstance();
break;
case 1:
return Fragment2.newInstance();
break;
}
}
#Override
public int getCount() {
return mNumOfTabs;
}
}
EDIT
ActivityPresenter:
public class ActivityPresenter implements Presenter<ActivityView>,
Interactor.OnFinishedListener<Response> {
private static final String TAG = "FW.ActivityPresenter";
#Inject
QueryPreferences mQueryPreferences;
private Interactor mInteractor;
private ActivityView mView;
private NetworkService mNetworkService;
private boolean mUseCache;
private String mQuery;
private int mPage;
private PublishSubject<Response> mPublishSubject = PublishSubject.create();
Observable<Response> getObservableResults() {
return mPublishSubject;
}
#Inject
public ActivityPresenter(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}
public void onSearchQueryListener(String query, int page) {
mQuery = mQueryPreferences.getStoredQuery();
mUseCache = query.equals(mQuery);
if (!mUseCache) {
mQueryPreferences.setStoredQuery(query);
Log.d(TAG, "Query added to cache: " + query);
}
mPage = page;
mInteractor.loadResults(this, query, false, page);
}
#Override
public void onFinished(Response response) {
if (mView != null) {
mPublishSubject.onNext(response);
}
}
#Override
public void onError(Throwable throwable) {
if (mView != null) {
mView.showMessage(throwable.getMessage());
}
}
#Override
public void attachView(ActivityView mvpView) {
mView = mvpView;
}
#Override
public void detachView() {
mView = null;
mInteractor.unSubscribe();
}
}
InteractorImpl:
public class InteractorImpl implements Interactor {
private static final String TAG = "FW.InteractorImpl";
private NetworkService mNetworkService;
private Subscription mSubscription;
public InteractorImpl(NetworkService networkService) {
mNetworkService = networkService;
}
#Override
public void loadResults(final OnFinishedListener listener, String query, boolean useCache, int page) {
Observable<Response> responseObservable = (Observable<Response>)
mNetworkService.getObservable(mNetworkService.getAPI().getResponseObservable(query, page), Response.class, true, useCache);
mSubscription = responseObservable.subscribe(new Observer<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
listener.onError(e);
}
#Override
public void onNext(Response response) {
listener.onFinished(response);
}
});
}
public void unSubscribe() {
if(mSubscription != null && !mSubscription.isUnsubscribed()) {
mSubscription.unsubscribe();
}
}
}
FragmentPresenter:
public class FragmentPresenter implements Presenter<FragmentView>,
Interactor.OnFinishedListener<Response> {
private static final String TAG = "FW.FragmentPres";
#Inject
QueryPreferences mQueryPreferences;
private Interactor mInteractor;
private FragmentView mView;
private NetworkService mNetworkService;
private ActivityPresenter mActvityPresenter;
#Inject
public FragmentPresenter(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}
void attachRecipeActivityPresenter(ActivityPresenter activityPresenter) {
mActvityPresenter = activityPresenter;
mActvityPresenter.getObservableResults().subscribe(data -> showData(data));
}
private void showData(Response response) {
if (response.getResults().getModels().isEmpty() && mPage == 0) {
mView.showNoResults();
} else {
mView.showResults(response.getResults().getModels());
}
}
#Override
public void onError(Throwable throwable) {
if (mView != null) {
mView.hideProgressBar();
mView.showMessage(throwable.getMessage());
}
}
#Override
public void attachView(FragmentView mvpView) {
mView = mvpView;
}
#Override
public void detachView() {
mView = null;
mInteractor.unSubscribe();
}
}
Using Retrofit2 and RxAndroid your method will look like this:
public void updateResults(String data) {
yourRetrofitAPI.getSomething(data)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(() -> {
// show your progress dialog
})
.subscribe(result -> {
// pass result to your view
}, error -> {
// hide your progress dialog
// get error message and send to your view
}, () -> {
// hide your progress dialog
});
}
interface YourRetrofitAPI {
#GET("/yourResource/{data}")
Observable<String> getSomething(#Path("data") String data);
}
So, about notify your fragments, with MVP you can make presenter fragments observe a stream from activity presenter, so both fragments will be notified when you query ends.
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
ActivityPresenter activityPresenter = new ActivityPresenter();
Fragment1Presenter fragment1Presenter = new Fragment1Presenter();
Fragment2Presenter fragment2Presenter = new Fragment2Presenter();
fragment1Presenter.attachActivityPresenter(activityPresenter);
fragment2Presenter.attachActivityPresenter(activityPresenter);
Observable.range(1, 10)
.delay(2, TimeUnit.SECONDS, Schedulers.immediate())
.subscribe(integer -> activityPresenter.queryData("query: " + integer));
}
class ActivityPresenter {
PublishSubject<String> publishSubject = PublishSubject.create();
Observable<String> serverDataAsObservable() {
return publishSubject.map(s -> String.format("%d - %s", System.currentTimeMillis(), s));
}
void queryData(String input) {
// based on your input you should query data from server
// and then emit those data with publish subject
// then all subscribers will receive changes
publishSubject.onNext(input);
}
}
class Fragment1Presenter {
private ActivityPresenter activityPresenter;
void attachActivityPresenter(ActivityPresenter activityPresenter) {
this.activityPresenter = activityPresenter;
this.activityPresenter.serverDataAsObservable()
.subscribe(data -> showData(data));
}
private void showData(String data) {
System.out.println("showing data on fragment1 with " + data);
}
}
class Fragment2Presenter {
private ActivityPresenter activityPresenter;
void attachActivityPresenter(ActivityPresenter activityPresenter) {
this.activityPresenter = activityPresenter;
this.activityPresenter.serverDataAsObservable()
.subscribe(data -> showData(data));
}
private void showData(String data) {
System.out.println("showing data on fragment2 with " + data);
}
}
}
Hope that it helps.
Best regards.
I'm trying to follow the Nest SDK on github and the sample code. My fragment code is as follows -
/**
* A placeholder fragment containing a simple view.
*/
public class ThermoActivityFragment extends Fragment implements View.OnClickListener {
private static final String TAG = ThermoActivity.class.getSimpleName(); // for log
// Nest API instance holder
private NestAPI tNest;
private NestToken tToken;
private Thermostat tThermo;
private Structure tStruct;
// Save the ID's and secret
private static final String CLIENT_ID = Constants.CLIENT_ID;
private static final String CLIENT_SECRET = Constants.CLIENT_SECRET;
private static final String REDIRECT_URL = Constants.REDIRECT_URL;
private static final int AUTH_TOKEN_REQUEST_CODE = 111;
private static final int RESULT_OK = -1;
private static final String THERMOSTAT_KEY = "thermostat_key";
private static final String STRUCTURE_KEY = "structure_key";
private static final String DEG_F = "%d°F";
// Text View
private TextView tTempIncr;
private TextView tTempDecr;
private TextView tSetTemp;
public ThermoActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_thermo, container, false);
tTempIncr = (TextView) view.findViewById(R.id.temp_incr);
tTempDecr = (TextView) view.findViewById(R.id.temp_decr);
tSetTemp = (TextView) view.findViewById(R.id.temp_value);
view.findViewById(R.id.temp_incr).setOnClickListener(this);
view.findViewById(R.id.temp_decr).setOnClickListener(this);
NestAPI.setAndroidContext(getContext());
tNest = NestAPI.getInstance();
tNest.setConfig(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
// Auth flow
tToken = ThermoSettings.loadAuthToken(getContext());
if (tToken != null) {
authenticate(tToken);
} else {
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
if (savedInstanceState != null) {
tThermo = savedInstanceState.getParcelable(THERMOSTAT_KEY);
tStruct = savedInstanceState.getParcelable(STRUCTURE_KEY);
//updateViews();
}
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(THERMOSTAT_KEY, tThermo);
outState.putParcelable(STRUCTURE_KEY, tStruct);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode != RESULT_OK || requestCode != AUTH_TOKEN_REQUEST_CODE) {
Log.e(TAG, "Finished with no result.");
return;
}
tToken = NestAPI.getAccessTokenFromIntent(intent);
if (tToken != null) {
ThermoSettings.saveAuthToken(getContext(), tToken);
authenticate(tToken);
} else {
Log.e(TAG, "Unable to resolve access token from payload.");
}
}
#Override
public void onStop() {
Log.d(TAG, "onStop");
super.onStop();
tNest.removeAllListeners();
}
#Override
public void onClick(View v) {
if (tThermo == null || tStruct == null)
return;
String tThermoId = tThermo.getDeviceId();
long temp = tThermo.getTargetTemperatureF();
switch (v.getId()) {
case R.id.temp_incr:
System.out.println("Temp Incr");
++temp;
tSetTemp.setText(String.format(DEG_F, temp));
tNest.thermostats.setTargetTemperatureF(tThermoId, temp);
break;
case R.id.temp_decr:
--temp;
tSetTemp.setText(String.format(DEG_F, temp));
tNest.thermostats.setTargetTemperatureF(tThermoId, temp);
break;
}
}
/**
* Authenticate with the Nest API and start listening for updates.
*
* #param token the token used to authenticate.
*/
private void authenticate(NestToken token) {
//NestAPI nest = NestAPI.getInstance();
tNest.authWithToken(token, new NestListener.AuthListener() {
#Override
public void onAuthSuccess() {
Log.v(TAG, "Authentication succeeded.");
fetchData();
}
#Override
public void onAuthFailure(NestException exception) {
Log.e(TAG, "Authentication failed with error: " + exception.getMessage());
ThermoSettings.saveAuthToken(getActivity(), null);
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
#Override
public void onAuthRevoked() {
Log.e(TAG, "Auth token was revoked!");
ThermoSettings.saveAuthToken(getActivity(), null);
tNest.launchAuthFlow(getActivity(), AUTH_TOKEN_REQUEST_CODE);
}
});
}
/**
* Setup global listener, start listening, and update view when update received.
*/
private void fetchData() {
tNest.addGlobalListener(new NestListener.GlobalListener() {
#Override
public void onUpdate(#NonNull GlobalUpdate update) {
tThermo = update.getThermostats().get(0);
//System.out.println(tThermo);
tStruct = update.getStructures().get(0);
//updateViews();
}
});
}
}
The Settings file where I save the token is as follows -
public class ThermoSettings {
private static final String TOKEN_KEY = "token";
private static final String EXPIRATION_KEY = "expiration";
public static void saveAuthToken(Context context, NestToken token) {
if (token == null) {
getPrefs(context).edit().remove(TOKEN_KEY).remove(EXPIRATION_KEY).commit();
return;
}
getPrefs(context).edit()
.putString(TOKEN_KEY, token.getToken())
.putLong(EXPIRATION_KEY, token.getExpiresIn())
.commit();
}
public static NestToken loadAuthToken(Context context) {
final SharedPreferences prefs = getPrefs(context);
final String token = prefs.getString(TOKEN_KEY, null);
final long expirationDate = prefs.getLong(EXPIRATION_KEY, -1);
if (token == null || expirationDate == -1) {
return null;
}
return new NestToken(token, expirationDate);
}
private static SharedPreferences getPrefs(Context context) {
return context.getSharedPreferences(NestToken.class.getSimpleName(), 0);
}
}
What I'm trying to do -
I'm using a button on the homepage to enter the Activity. When I press the button, I see the Nest Authorization webpage, When I click on Accept, I see my UI but don't see the 'Authentication Succeeded' message in the log.
Can someone tell me what I'm doing wrong?
I finally got it to work. Earlier I was trying to make it work from a fragment. After I deleted the fragment and moved the code to MainActivity, it started to work.