How correctly apply MVP pattern in my sample - java

I decide to learn about MVP pattern and after look through some articles i want to try it with my current project.
I have choosen one activity and begin to think how i can decouple it according MVP rules. And eventually I don't know how to do it. It seems like a not complicated activity but I don't know
Could please someone adviced me with what I have to start?
Which methods have to be in presenter, witch view have to be left in this current activity and whitch methods have to be in interface?
Just advised me who i supposed to begin.
This is my class
public final class ActivityUserDataScreen extends AppCompatActivity implements InterfaceActivityUserDataScreen{
private static String gender;
private static int inputHeight;
private static int inputWeight;
private TextInputLayout tilUserName;
private int backPressedQ = 0;
private String avatarName;
private static final String MEN = "men";
private static final String WOMEN = "men";
private Context context;
private PresenterActivityUserDataScreen presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Crashlytics());
setContentView(R.layout.activity_user_data_screen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setSupportActionBar((Toolbar) findViewById(R.id.tool_bar));
context = getApplicationContext();
initNumberPicker();
initVar();
presenter = new PresenterActivityUserDataScreen(this);
}
private void initNumberPicker() {
NumberPicker pickerHeight = (NumberPicker) findViewById(R.id.pickerHeight);
UtilClass.setDividerColor(pickerHeight, UtilClass.getMyColor(context, R.color.ntz_color_yellow));
pickerHeight.setOnValueChangedListener(changeListener);
pickerHeight.setMaxValue(220);
pickerHeight.setMinValue(130);
pickerHeight.setValue(States.HEIGHT_DEFAULT);
NumberPicker pickerWeight = (NumberPicker) findViewById(R.id.pickerWeight);
UtilClass.setDividerColor(pickerWeight, UtilClass.getMyColor(context, R.color.ntz_color_yellow));
pickerWeight.setOnValueChangedListener(changeListener);
pickerWeight.setMaxValue(120);
pickerWeight.setMinValue(35);
pickerWeight.setValue(States.WEIGHT_DEFAULT);
}
private void initVar() {
tilUserName = (TextInputLayout) findViewById(R.id.tilUserName);
SwitchButton switchButton = (SwitchButton) findViewById(R.id.sb_custom);
switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked){
gender = WOMEN;
}else {
gender = MEN;
}
}
});
EditText etAvatarName = (EditText) findViewById(R.id.etAvatarName);
etAvatarName.setText(getResources().getString(R.string.avatar));
}
private NumberPicker.OnValueChangeListener changeListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
switch (picker.getId()) {
case R.id.pickerHeight:
inputHeight = newVal;
break;
case R.id.pickerWeight:
inputWeight = newVal;
break;
}
}
};
#Override
public final void onBackPressed() {
UtilClass.processClick(context);
if (backPressedQ == 1) {
backPressedQ = 0;
super.onBackPressed();
overridePendingTransition(R.anim.open_main, R.anim.close_next);
} else {
backPressedQ++;
Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT).show();
}
//Обнуление счётчика через 5 секунд
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
backPressedQ = 0;
}
}, 5000);
}
public final void goNext(View view) {
UtilClass.processClick(context);
EditText editText = tilUserName.getEditText();
Editable editable = null;
if (editText != null) {
editable = editText.getText();
}
if (editable != null) {
avatarName = editable.toString();
}
if (!isValidAvatarName()) return;
saveUserData();
MetadataSaver saver = new MetadataSaver(context);
saver.saveFirstUserInfo();
saver.saveDeviceInfo();
PreferencesHelper.savePref(context, States.STILL_NOT_FINISH, true);
UtilClass.goToNextActivity(ActivityUserDataScreen.this, ActivityVideo.class);
}
private void saveUserData(){
saveAvatarGender();
saveAvatarHeight();
saveAvatarWeight();
saveAvatarName();
}
private void saveAvatarGender(){
if (gender == null){
gender = MEN;
}
PreferencesHelper.savePref(context, States.AVATAR_GENDER, gender);
}
private boolean isValidAvatarName() {
if (UtilClass.isTextEmpty(avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.fill_your_avatar_name));
return false;
}
if (avatarName.contains(" ")) {
avatarName = avatarName.replace(" ", "");
}
if (!UtilClass.isLatinAlphabet(avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.avatar_name_in_english));
return false;
}
if (!UtilClass.isNameFree(context, avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.avatar_name_already_in_use));
return false;
}
return true;
}
private void saveAvatarHeight() {
int result;
if (inputHeight == 0) {
result = States.HEIGHT_DEFAULT;
} else {
result = inputHeight;
}
PreferencesHelper.savePref(context, States.AVATAR_HEIGHT, result);
}
private void saveAvatarWeight() {
int result;
if (inputWeight == 0) {
result = States.WEIGHT_DEFAULT;
} else {
result = inputWeight;
}
PreferencesHelper.savePref(context, States.AVATAR_WEIGHT, result);
}
private void saveAvatarName() {
PreferencesHelper.savePref(context, States.AVATAR_NAME, avatarName);
}
public final void switchManWoman(View view) {
UtilClass.processClick(context);
}
}
Thanks in advance!

The things to take into account are:
The view needs to be as dumb as possible. Think of it as an executor of the commands given by the presenter, and reporter to the presenter of all the stuff that happened on the UI. The interface should provide methods like "display this text", and / or calling presenter's methods like "the button was clicked".
the presenter is the one in command. It drives your view behaviour and reacts to the inputs coming from the view itself. Ideally, it should abstract from anything Android related, in this way you can test the behaviour inside vanilla tests.

Google has published a collection of samples to discuss and showcase different architectural tools and patterns for Android apps.
To begin, very usefull to you to understand how this one works. And adapt to your sample.
[...] This sample is the base for many of the variants. It showcases a simple implementation of the Model-View-Presenter pattern with no architectural frameworks. It uses manual dependency injection to provide a repository with local and remote data sources. Asynchronous tasks are handled with callbacks [...]

I highly recommend reading this article on medium: https://medium.com/#tinmegali/model-view-presenter-mvp-in-android-part-1-441bfd7998fe#.f4yiylrwa .
In essence, all things related to the android SDK should be put in your "view" (and occasionally your model), which will usually be a fragment or activity. Figuring out the difference between your model and presenter will be more up to you, however, you can think about your presenter as the thing that makes program logic decisions based on inputs to your application. Often, the mvp pattern is used in Android development to try to get around rotation and activity recreation issues so you may have luck using a static presenter for a small sample application.
Best of luck!

Related

How to instatiate dialogfragment in a project

I am done with a flag quiz app, it turns out however, that the app doesn't run smoothly. I have debugged several times. Presently, I am stuck. Initially the exception was fragment null must be public static in order to recreate Instance..or something like that. After several, several hours of surfing the net for solutions I created a new class, there is no error in this class but it is coloured red. Why is this?, also what do I need to use the complete or correct this code. eg Studio is saying void showDialog() is not used below is a portion of my MainActivity Fragment
guessButton = ((Button) v);
String guess = guessButton.getText().toString();
String answer = getCountryName(correctAnswer);
++totalGuesses;
if (guess.equals(answer)) {
++correctAnswers;
answerTextView.setText(answer + "!");
answerTextView.setTextColor(
getResources().getColor(R.color.correct_answer));
disableButtons();
if (correctAnswers == FLAGS_IN_QUIZ) {
resetQuiz();
void showDialog(){
DialogFragment newFragment = MyNewFragment.newInstance();
newFragment.show(getFragmentManager(), "QUIZ RESULTS");
newFragment.setCancelable(false);
}
public void doPositiveClick(){
Log.i(" fragmentdialog" , "positive click");
}
}
else {
handler.postDelayed(
new Runnable() {
#Override
public void run() {
animate(true);
}
}, 2000);
}
} else {
flagImageView.startAnimation(shakeAnimation);
answerTextView.setText((R.string.incorrect_answer));
answerTextView.setTextColor(ContextCompat.getColor(getContext(),
R.color.incorrect_answer));
guessButton.setEnabled(false);
}
}
;
private void disableButtons() {
for (int row = 0; row < guessRows; row++) {
LinearLayout guessRow = guessLinearLayouts[row];
for (int i = 0; i < guessRow.getChildCount(); i++)
guessRow.getChildAt(i).setEnabled(false);
}
}
};
}
Below is the new class that keeps on appearing red.
public class MyNewFragment extends DialogFragment {
public static MyNewFragment newInstance() {
MyNewFragment frag = new MyNewFragment();
Bundle args = new Bundle();
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(getActivity())
.setMessage("fff")
.setPositiveButton(R.string.reset_quiz,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
}
);
return builder.create();
}
};
I will appreciate a precise answer that helps solve these problems
Looks to me that you are somehow declaring methods within if-then-else clauses, hence Android Studio is saying the are unused. The snippet of code where you attempt to launch the DialogFragment should look more like this (i.e. without the surrounding void showDialog() {} method definition):
if (correctAnswers == FLAGS_IN_QUIZ) {
resetQuiz();
DialogFragment newFragment = MyNewFragment.newInstance();
newFragment.show(getFragmentManager(), "QUIZ RESULTS");
newFragment.setCancelable(false);
}
I can't see anything significantly wrong with your definition of MyNewFragment, although you have an empty OnClickListener.OnClick() definition and you are setting the fragment arguments to an empty Bundle. However neither of these issues would generate a Studio compiler error making it show red. Find where Studio is indicating the errors and it will tell you what's wrong with your code.

UnityPlayerActivity global initialization

I'd appreciate if you could advise on my problem.
I'm working on AR app using Vuforia SDK for Unity3D and Android plugins.
I have several ImageTargets and 3D models on my scene.
My class that works with android plugin looks like this:
public class AssetBundleAugmenter : MonoBehaviour, ITrackableEventHandler
{
void Start()
{
StartCoroutine(DownloadAndCache());
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour)
{
mTrackableBehaviour.RegisterTrackableEventHandler(this);
}
init();
}
public void OnTrackableStateChanged(
TrackableBehaviour.Status previousStatus,
TrackableBehaviour.Status newStatus)
{
if (newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
if (!mAttached && mBundleInstance)
{
// if bundle has been loaded, let's attach it to this trackable
//...
}
OnTrackingFound();
}
else
{
OnTrakingLost();
}
}
private void OnTrackingFound()
{
if (mTrackableBehaviour is ImageTargetAbstractBehaviour)
{
GetJavaObject().Call("OnMarkerFound");
}
}
void onButtonClicked(int index)
{
//Changing current 3D model material
}
#if UNITY_ANDROID
private AndroidJavaObject javaObj = null;
//LISTENING TO BUTTON CLICK EVENTS FROM ANDROID
private sealed class EventListner : AndroidJavaProxy
{
private AssetBundleAugmenter mReceiver;
public EventListner(AssetBundleAugmenter receiver)
: base("com.mypackage.myapp.ImageTargetTracker$Listner")
{
mReceiver = receiver;
}
public void onButtonClicked(int index) //change color of model
{
mReceiver.onButtonClicked(index);
}
}
private AndroidJavaObject GetJavaObject()
{
if (javaObj == null)
{
javaObj = new AndroidJavaObject("com.mypackage.myapp.ImageTargetTracker");
}
return javaObj;
}
AndroidJavaObject activity;
private void init()
{
// Retrieve current Android Activity from the Unity Player
AndroidJavaClass jclass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
activity = jclass.GetStatic<AndroidJavaObject>("currentActivity");
// Pass reference to the current Activity into the native plugin,
GetJavaObject().Call("setActivity", activity, new EventListner(this));
}
#else
void init() {}
#endif
}
So I attached this script to all of my ImageTargets on the scene, which I know must be wrong, because UnityPlayer gets initialized several times in my init() function.
I tried to attach the script to ARCamera on my scene, and write initialization only there, but I'm not sure how to access currentActivity in scripts that work with ImageTargets. Also, I use listener - the interface in my plugin that listens to button clicks to fire some functionality in unity.
My plugin code:
public class ImageTargetTracker {
public static interface Listner {
public void onButtonClicked(int index);
}
private Listner mListner;
protected Activity mCurrentActivity;
public void setActivity(Activity activity, Listner listner)
{
mCurrentActivity = activity;
mListner = listner;
mCurrentActivity.runOnUiThread(new Runnable() {
public void run() {
LayoutInflater inflater = mCurrentActivity.getLayoutInflater();
Resources resources = mCurrentActivity.getResources();
String pkgName = mCurrentActivity.getPackageName();
int id = resources.getIdentifier("camera_layout", "layout", pkgName);
View view = inflater.inflate(id, null);
mCurrentActivity.addContentView(view, param);
//INITIALIZING UI ELEMENTS HERE (DISPLAYED ON TOP OF CAMERA)
}
public void OnMarkerFound(){
mCurrentActivity.runOnUiThread(new Runnable() {
public void run() {
//Showing some UI elements
}
});
}
}
So, how can I globally initialize the Activity and my plugin class in Unity one time, and use them in all of my scripts?
As discussed in the comments, I recommend using the singleton pattern.

Wait for completion of a calculation without UI freeze

I am trying to implement a search function in an Android app that takes text from an AutoCompleteTextView, waits if there hasn't been made a change in the last 1.5 seconds and shows the search results. For this I use the TextWatcher class.
However, all my tries to implement this behavior ran into trouble with some functions only being allowed in the UI thread itself (via runOnUIThread) or the thread having Looper.prepare() called before.
In all attempts, the app crashes randomly when entering additional characters or deleting some, does not show any search results or reload to the start activity.
The following is a simplyfied recreation of my most recent try, where I use a Handler.
search.getResults is the long computation and matches is an array that has to be filled before delayableAdapterCreation creates the ArrayAdapterWithSpaceFilter.
public class SearchFragment extends Fragment {
public final static int MAX_NUMBER_OF_SUGGESTIONS = 4; // only show a max of 4 suggestions if more were found
public final static int SEARCH_CHAR_AMOUNT = 3; // only search if at least 3 characters were typed
public final static long SEARCH_DELAY_MILLIS = (long) 1500; // the time to wait for no text changes in milliseconds
private Search search;
private AutoCompleteTextView textView;
private String[] matches;
private String userStartRequest;
private Entry[] suggestions;
private FragmentListenter sListener;
private EntryFunctions ef = new EntryFunctions();
private Runnable delayableSearch;
private Runnable delayableAdapterCreation;
private Handler delayableSearchHandler;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
delayableSearchHandler = new Handler();
delayableSearch = new Runnable() {
#Override
public void run() {
userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
suggestions = search.getResults(userStartRequest);
matches = ef.fillMatches(suggestions);
}
};
delayableAdapterCreation = new Runnable() {
#Override
public void run() {
ArrayAdapterWithSpaceFilter<String> adapter =
new ArrayAdapterWithSpaceFilter<String>(getActivity(),
android.R.layout.simple_list_item_1,
matches);
textView.setAdapter(adapter);
}
};
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search, container, false);
}
#Override
public void onStart() {
super.onStart();
textViewHandler();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof FragmentListenter)) throw new AssertionError();
sListener = (FragmentListenter) context;
}
/**
* Interface for communicate to activity
*/
public interface FragmentListenter {
void onFragmentFinish(String userStartRequest);
}
/**
* Handler for the AutoCompleteTextView
*/
private void textViewHandler() {
try {
textView = (AutoCompleteTextView) getView().findViewById
(R.id.startNaviAutoCompleteTextView);
search = new Search();
System.out.println("Created Search object");
textView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("TextWatcher beforeTextChanged");
}
#Override
public void onTextChanged(CharSequence s, final int start, int before, int count) {
delayableSearchHandler.removeCallbacks(delayableSearch); userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
if (textView.getText().length() >=
SEARCH_CHAR_AMOUNT) {
new Thread(delayableSearch).start();
delayableSearchHandler.postDelayed
(delayableAdapterCreation, SEARCH_DELAY_MILLIS);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
At this point, it does not matter to me, whether the calculation already starts whenever a new character is typed into the AutoCompleteTextView and an eventual old search is canceled or the search starts after the 1.5 seconds.
The above code does crash if the search term yields no results and there are problems with the results list. Sometimes it shows up for what has been entered a few keystrokes ago (so if I search for abcd slowly I get search results for abc), sometimes it doesn't show up at all. My guess would be a race condition or some problem with calling the textViewHandler or onTextChanged methods multiple times, even though delayableSearchHandler.removeCallbacks(delayableSearch) should prevent this from happening.
Can anyone explain, what the interaction between the worker thread and the UI thread would have to look like, so it is guaranteed that the search delivers it's results?
Thanks in advance,
Joe
Any long running operation (Network call, database search...) can take long time to execute thus blocking the UI. Prior to Ice cream sandwich this kind of behavior was tolerated by the android runtime.
This article might be a good read

Putting and getting savedInstanceState data

I'm just trying to understand the Activity lifetime with this simple example by trying to find how I get back the SomeIntegers g_values object and the ArrayList AnInteger objects within it.
As it is, it is not of much meaning but will serve as a paradigm of my real situation where initial setup requires the app to schlepp through countless reams of pre-processing eg access and list fonts, analyse all my available games, in the APK, on file and online in my website, players records etc. The final app is a system of games and activities to help SpLD (dyslexia) students of all ages exercise their reading, spelling, organisational skills and short term memory. It is of serious intent. Although free running, it is best used with SpLD supervisors/tutors who can set the work schedule of their charge and even add their own games.
Anyway enough of the irrelevant background.
Can I save my somewhat complex objects using access to the savedInstanceState (somewhat hampered by their being no putxxxxx method of the correct form) or should abandon this approach and recover the data from persistent files or databases? This can be discussed hopefully within the limits of this simple example, the real thing is simply more of the same but with different details.
Note added after. There is also the issue of taking the user/player back to where he/she was when the app experienced the need to save its InstanceState. As the major influence seems to be the orientation of the tablet, I could maybe side step that by locking the orientation at start up. This would simplify many display issues also but is it an "unacceptable" style?
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class TestBundle extends AppCompatActivity {
SomeIntegers g_values;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("onCreate (" + (savedInstanceState == null ? "null)" : "set)"));
if (savedInstanceState == null)
{ g_values = new SomeIntegers();
String result = g_values.report();
System.out.println("Startup Result: " + result);
setContentView(R.layout.activity_test_bundle); // Where do I put this line?
}
else
{ //Do I get g_values back here?
//More relevantly, can I, and how can I, put g_values in the
//savedInstanceState when onSaveInstanceState is called?
String result = g_values.report();
System.out.println("Result: " + result);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
System.out.println("onSaveInstanceState (" + (outState == null ? "null)" : "set)"));
//How do I add g_values to the Bundle?
}
// Following is just stuff to watch the progress of the
// Activity in the ADB Log. Not of much relevance. Or is it?
#Override
protected void onStop() {
super.onStop();
System.out.println("onStop()");
}
#Override
protected void onStart() {
super.onStart();
System.out.println("onStart()");
}
#Override
protected void onRestart() {
super.onRestart();
System.out.println("onRestart()");
}
#Override
protected void onResume() {
super.onResume();
System.out.println("onResume()");
}
#Override
protected void onPause() {
super.onPause();
System.out.println("onPause()");
}
#Override
protected void onDestroy() {
super.onDestroy();
System.out.println("onDestroy()");
}
}
public class SomeIntegers {
private ArrayList<AnInteger> c_values;
SomeIntegers() {
c_values = new ArrayList<AnInteger>();
c_values.add (new AnInteger(1));
c_values.add (new AnInteger(2));
c_values.add (new AnInteger(3));
c_values.add (new AnInteger(4));
c_values.add (new AnInteger(29));
c_values.add (new AnInteger(30));
}
String report() {
String g = "";
for (AnInteger ai : c_values) {
if (!g.isEmpty()) g = g + ", ";
g = g + ai.getC_value();
}
return (g.isEmpty() ? "Empty" : g);
}
}
public class AnInteger {
private int c_value;
AnInteger(int value) { c_value = value); }
public int getC_value () { return c_value; }
}
Thank you. Josie Hill
First make your data models implement Parcelable :
AnInteger:
public class AnInteger implements Parcelable {
private int c_value;
public AnInteger(int value) {
this.c_value = value;
}
public int getC_value() {
return c_value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.c_value);
}
protected AnInteger(Parcel in) {
this.c_value = in.readInt();
}
public static final Parcelable.Creator<AnInteger> CREATOR = new Parcelable.Creator<AnInteger>() {
#Override
public AnInteger createFromParcel(Parcel source) {
return new AnInteger(source);
}
#Override
public AnInteger[] newArray(int size) {
return new AnInteger[size];
}
};
}
SomeIntegers:
public class SomeIntegers implements Parcelable {
private ArrayList<AnInteger> c_values;
public SomeIntegers() {
c_values = new ArrayList<>();
c_values.add(new AnInteger(1));
c_values.add(new AnInteger(2));
c_values.add(new AnInteger(3));
c_values.add(new AnInteger(4));
c_values.add(new AnInteger(29));
c_values.add(new AnInteger(30));
}
public String report() {
String g = "";
for (AnInteger ai : c_values) {
if (!g.isEmpty()) {
g = g + ", ";
}
g = g + ai.getC_value();
}
return (g.isEmpty() ? "Empty" : g);
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(this.c_values);
}
protected SomeIntegers(Parcel in) {
this.c_values = in.createTypedArrayList(AnInteger.CREATOR);
}
public static final Parcelable.Creator<SomeIntegers> CREATOR = new Parcelable.Creator<SomeIntegers>() {
#Override
public SomeIntegers createFromParcel(Parcel source) {
return new SomeIntegers(source);
}
#Override
public SomeIntegers[] newArray(int size) {
return new SomeIntegers[size];
}
};
}
Then in your activity saving and restoring gets pretty easy, here is an example using your current data model:
//set up class fields/members
private final static String STATE_G_VALS = "STATE_G_VALS";
SomeIntegers g_values = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_bundle);
System.out.println("onCreate (" + (savedInstanceState == null ? "null)" : "set)"));
if (savedInstanceState != null) {
// get g_values back here
g_values = savedInstanceState.getParcelable(STATE_G_VALS);
}
if (g_values == null) {
// ok its null, lets make one
g_values = new SomeIntegers();
}
// log some stuff
String result = g_values.report();
System.out.println("Result: " + result);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
//set g_values to the Bundle/saved state (even if it is null)
outState.putParcelable(STATE_G_VALS, g_values);
super.onSaveInstanceState(outState);
}
The concept of restoring the activity states is based on device orientation. So for example if you pull some changes from persisted file, loaded it, when the screen changes it angle of rotation that data will be recreated. So the activity uses a bundle to wrap that data, and permits the user to save the current working state of such file, which then can be restored. Here is a great link. Your requirements sounds consistent as it regards data changes, as per my first question regarding the anticipated file sizes, your requirements sounds relatively small.
To work compound data types and abstract data types , do consider using GSON.which is a Java serialization/deserialization library to convert Java Objects into JSON and back
Therefore I can recommend you using the power of shared preferences in android.If you have a relatively small collection of key-values that you'd like to save, you should use the SharedPreferences APIs. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. In simple terms,Shared Preferences allow you to save and retrieve data in the form of key,value pair.
Android provides many ways of storing data of an application. If your requirements needs storage consistency, I would go with the database approach, I would recommend using realm.Realm is a mobile database and a replacement for SQLite. Although is an OO database it has some differences with other databases. Realm is not using SQLite as it’s engine. Instead it has own C++ core and aims to provide a mobile-first alternative to SQLite.
Hope this was helpful:)

UtteranceProgressListener won't call the functions

I am trying to make a speech powered app, however I have run into a major problem.
My UtteranceProgressListener Class will not call any of the given methods regardless of where I place the Speak method.
Here is my code:
This is my OnCreate Method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
voiceBtn = (Button) findViewById(R.id.startListeningBtn);
voiceBtn.setEnabled(false);
textToSpeech = new TextToSpeech(mContext,new botListener());
}
This is the OnInitListner Imeplementation
public class botListener implements TextToSpeech.OnInitListener{
#Override
public void onInit(int i) {
if(i == TextToSpeech.SUCCESS)
{
int s = textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String s) {
Toast.makeText(getApplicationContext(),"Done Speaking",Toast.LENGTH_SHORT).show();
}
#Override
public void onDone(String s) {
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}
#Override
public void onError(String s) {
Toast.makeText(getApplicationContext(),"Done Speaking",Toast.LENGTH_SHORT).show();
}
});
Log.d(TAG,String.valueOf(s));
int result = textToSpeech.setLanguage(Locale.ENGLISH);
if(result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
Log.e(TAG,"Language not supported");
Intent installLanguage = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installLanguage);
}
Log.d(TAG,"Started Voice Speaker");
}
else{
Log.e(TAG,"initialization failed");
}
}
}
Now, when I press the button, the event that fires is:
public void initVoiceRecog(View v){
//Toast.makeText(mContext,"Clicked",Toast.LENGTH_SHORT).show();
Speak("hello","1");
// does some other things here after that
}
private void Speak(String text,String identifierID){
if(Build.VERSION.SDK_INT>21) {
Bundle params = new Bundle();
params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,identifierID);
textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, params, identifierID);
}
else{
// ttsMap is a HashMap
ttsMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,identifierID);
textToSpeech.speak(text,TextToSpeech.QUEUE_FLUSH,ttsMap );
}
}
My Question is, after saying hello, it does not fire the OnStart() or the OnError() or the OnDone() methods. Why is this happening?
I tried with the deprecated setOnUtteranceListner() as well, same result. It does not fire any of the methods, the Toasts don't show up.
Please tell a fix or a workaround for this.
The Devices I tried on are:
API 19 Micromax Canvas Nitro
API 21 Samsung S4
API 23(Marshmellow) ASUS Zenfone
I finally figured out why the callbacks were not working. Turns out, they were working and calling on a separate thread. So to execute the normal functionality, call the functions in the 'Activity.this.RunOnUiThread' and put this in the call back fuctions.

Categories