JavaFX 2 and Internationalization - internationalization

I've just started writing my first JavaFX 2 application after learning the basics and would like to internationalize it.
I notice that in JavaFX 1.x, the scripting language allowed for very simple internationalization of strings. Are there any similar features in JavaFX 2?
Basically: what is the best practice for internationalizing a JavaFX 2 application?

The basic steps (among others) of a java app internationalizing, are Localelizing and resource bundling. In JavaFX, you can use FXMLLoader#setResources() for that purposes. Here a SSCCE demo to demonstrate it. The codes are self-descriptive.
Demo package structure:
bundledemo
|------ BundleDemo.java
|------ MyController.java
|------ MyView.fxml
bundles
|------ MyBundle_en.properties
|------ MyBundle_kg.properties
MyBundle_en.properties
key1=Name Surname
key2=How are you?
MyBundle_kg.properties
key1=Aты Жөнү
key2=Кандайсың?
MyView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.*?>
<BorderPane fx:controller="bundledemo.MyController" xmlns:fx="http://javafx.com/fxml">
<top>
<!-- This label's text will be set by the controller -->
<Label fx:id="lblTextByController"/>
</top>
<center>
<!-- This label's text will be taken from the bundle automatically -->
<Label text="%key2"/>
</center>
</BorderPane>
MyController.java
package bundledemo;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class MyController implements Initializable {
#FXML private Label lblTextByController;
private ResourceBundle bundle;
#Override
public void initialize(URL location, ResourceBundle resources) {
bundle = resources;
lblTextByController.setText(bundle.getString("key1"));
}
}
BundleDemo.java
package bundledemo;
// imports are ignored.
public class BundleDemo extends Application {
private Stage stage;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
Button btnEN = new Button();
btnEN.setText("English");
btnEN.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
loadView(new Locale("en", "EN"));
}
});
Button btnKG = new Button();
btnKG.setText("Kyrgyz");
btnKG.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
loadView(new Locale("kg", "KG"));
}
});
VBox root = new VBox(20);
root.getChildren().add(HBoxBuilder.create().spacing(10).style("-fx-background-color: gray").padding(new Insets(5)).children(btnEN, btnKG).build());
root.getChildren().add(new StackPane());
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
private void loadView(Locale locale) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setResources(ResourceBundle.getBundle("bundles.MyBundle", locale));
Pane pane = (BorderPane) fxmlLoader.load(this.getClass().getResource("MyView.fxml").openStream());
// replace the content
StackPane content = (StackPane) ((VBox) stage.getScene().getRoot()).getChildren().get(1);
content.getChildren().clear();
content.getChildren().add(pane);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot:
If your internationalized text needs to be rendered in a font that might be on the user's target system, then you can either:
Embed the font with your application:
How to embed .ttf fonts is JavaFx 2.2?
OR
Use web(Google) fonts in JavaFX.
If the required font is not available, then the internationalized text might be displayed as unintelligible gibberish, even though everything else about the setup is fine.

This works for me:
└───src
├───app
├───bundles // <- here the "bundles"
├───dicts
├───images
├───libs
└───resources
In the bundles package are
LangBundle_en.properties
LangBundle_de.properties
Sample content:
enter_pwd=Enter your password:
To load them I use the following code:
#Override
public void initialize(URL location, ResourceBundle resources) {
ResourceBundle lngBndl = ResourceBundle
.getBundle("bundles.LangBundle", new Locale("en", "EN"));
tvSetupPwd.setText(lngBndl.getString("enter_pwd"));
// ...
}

Look at my example
More I described here or on GitHub
Update:
the solution is in Messages.java
/**
* The class with all messages of this application.
*/
public abstract class Messages {
private static ResourceBundle BUNDLE;
private static final String FIELD_NAME = "lookup";
private static final String BUNDLE_NAME = "messages/messages";
private static final String CONTROLS_BUNDLE_NAME = "com/sun/javafx/scene/control/skin/resources/controls";
public static final String MAIN_APP_TITLE;
public static final String DIALOG_HEADER;
public static final String MAIN_CONTROLLER_CONTENT_TEXT;
public static final String MAIN_CONTROLLER_HELLO_TEXT;
public static final String MAIN_CONTROLLER_GOODBYE_TEXT;
static {
final Locale locale = Locale.getDefault();
final ClassLoader classLoader = ControlResources.class.getClassLoader();
final ResourceBundle controlBundle = getBundle(CONTROLS_BUNDLE_NAME,
locale, classLoader, PropertyLoader.getInstance());
final ResourceBundle overrideBundle = getBundle(CONTROLS_BUNDLE_NAME,
PropertyLoader.getInstance());
final Map override = getUnsafeFieldValue(overrideBundle, FIELD_NAME);
final Map original = getUnsafeFieldValue(controlBundle, FIELD_NAME);
//noinspection ConstantConditions,ConstantConditions,unchecked
original.putAll(override);
BUNDLE = getBundle(BUNDLE_NAME, PropertyLoader.getInstance());
MAIN_APP_TITLE = BUNDLE.getString("MainApp.title");
DIALOG_HEADER = BUNDLE.getString("Dialog.information.header");
MAIN_CONTROLLER_CONTENT_TEXT = BUNDLE.getString("MainController.contentText");
MAIN_CONTROLLER_HELLO_TEXT = BUNDLE.getString("MainController.helloText");
MAIN_CONTROLLER_GOODBYE_TEXT = BUNDLE.getString("MainController.goodbyeText");
}
public static ResourceBundle GetBundle() {
return BUNDLE;
}
}
and in PropertyLoader.java
public class PropertyLoader extends ResourceBundle.Control {
private static final String PROPERTIES_RESOURCE_NAME = "properties";
private static final PropertyLoader INSTANCE = new PropertyLoader();
public static PropertyLoader getInstance() {
return INSTANCE;
}
#Override
public ResourceBundle newBundle(final String baseName, final Locale locale, final String format,
final ClassLoader loader, final boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
final String bundleName = toBundleName(baseName, locale);
final String resourceName = toResourceName(bundleName, PROPERTIES_RESOURCE_NAME);
ResourceBundle bundle = null;
InputStream stream = null;
if (reload) {
final URL url = loader.getResource(resourceName);
if (url != null) {
final URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
try {
bundle = new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
} finally {
stream.close();
}
}
return bundle;
}
}

Related

Kafka Stream: can't get data from Kafka persistent keyValue state store

I am using Kafka streams and persistent KeyValue store in my application. There are two KeyValue stores I am using and two processors. I am facing issue with the stateStore which is shared between two processors. NameProcessor put data into nameStore and EventProcessor extracts data from nameStore. From Debugging it looks like, NameProcessor is able to put data successfully but when EventProcessor trying to get data from nameStore, it doesn't get any data. Below is the code snippet for Application class, Topology, NameProcessor and EventProcessor. Also, I am using Spring boot parent version 2.4.3, kafka-streams version 2.2.0 and kafka-clients version 2.2.0
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
Properties configs = getKafkaStreamProperties();
Topology builder = new Topology();
new ApplicationTopology(builder);
KafkaStreams stream = new KafkaStreams(builder, configs);
stream.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
// here you should examine the throwable/exception and perform an appropriate action!
logger.error("Uncaught exception in stream, MessageDetail: "+ ExceptionUtils.getRootCauseMessage(throwable) + ", Stack Trace: " + throwable.fillInStackTrace());
Runtime.getRuntime().halt(1);
});
Runtime.getRuntime().addShutdownHook(new Thread(stream::close));
stream.start();
}
private static Properties getKafkaStreamProperties() {
Properties configs = new Properties();
configs.setProperty(StreamsConfig.APPLICATION_ID_CONFIG, getApplicationId());
configs.setProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, getBootstrapServers());
configs.setProperty(StreamsConfig.RETRIES_CONFIG, getRetries());
configs.setProperty(StreamsConfig.RETRY_BACKOFF_MS_CONFIG, getRetryBackOffMs());
configs.setProperty(StreamsConfig.REPLICATION_FACTOR_CONFIG, getReplicationFactor());
configs.setProperty(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, getMaxPollIntervalMs());
return configs;
}
public class ApplicationTopology {
public ApplicationTopology (Topology builder) {
StoreBuilder<KeyValueStore<String, Sensor>> nameStoreBuilder = Stores.
keyValueStoreBuilder(Stores.persistentKeyValueStore("nameStore"), Serdes.String(), CustomSerdes.getNameSerde()).withCachingEnabled().withLoggingEnabled(new HashMap<>());
StoreBuilder<KeyValueStore<String, String>> stateStoreBuilder = Stores.
keyValueStoreBuilder(Stores.persistentKeyValueStore("stateStore"), Serdes.String(), Serdes.String()).withCachingEnabled().withLoggingEnabled(new HashMap<>());
builder.addSource(AutoOffsetReset.LATEST, "source", Serdes.String().deserializer(), CustomSerdes.getIncomingEventSerde().deserializer(), getInboundTopic())
.addProcessor(TRANSFORMER, () -> new EventProcessor(), "source")
.addStateStore(nameStoreBuilder, TRANSFORMER)
.addSink("sink", getOutboundTopic(), Serdes.String().serializer(), CustomSerdes.getIncomingEventSerde().serializer(), TRANSFORMER);
//reset to earliest for model config topic as some models could be already on the topic
builder.addSource(AutoOffsetReset.EARLIEST, "nameStoreSource", Serdes.String().deserializer(), CustomSerdes.getSensorSerde().deserializer(), getInboundSensorUpdateTopic())
.addProcessor("process", () -> new NameProcessor(), "nameStoreSource")
.addStateStore(nameStoreBuilder, TRANSFORMER, "process");
}
public ApplicationTopology() {}
} }
public class NameProcessor extends AbstractProcessor<String, Sensor> {
private static final Logger LOGGER = LoggerFactory.getLogger(NameProcessor.class);
ProcessorContext context;
private KeyValueStore<String, Name> nameStore;
private static List<String> externalDeviceIdList = new ArrayList<>();
#Override
public void init(ProcessorContext processorContext) {
this.context = processorContext;
this.nameStore = (KeyValueStore<String, Name>) context.getStateStore("nameStore");
}
#Override
public void process(String externalDeviceId, Name name) {
if (StringUtils.isNotBlank(externalDeviceId)) {
String[] externalDeviceIds = SensorUtils.getExternalDeviceIdsWithoutSuffix(externalDeviceId);
if (Objects.isNull(name)) {
Arrays.stream(externalDeviceIds).forEach(id -> {
sensorStore.delete(id);
});
} else {
addOrUpdateNameInStore(sensor, externalDeviceIds);
}
}
}
private void addOrUpdateNameInStore(Sensor sensor, String[] externalDeviceIds) {
Arrays.stream(externalDeviceIds).forEach(id -> {
sensorStore.put(id, sensor);
});
// context.commit();
}
}
public class EventProcessor extends AbstractProcessor<String, IncomingEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(EventProcessor.class);
ProcessorContext context;
private KeyValueStore<String, Name> nameStore;
private KeyValueStore<String, String> stateStore;
#Override
public void init(ProcessorContext processorContext) {
this.context = processorContext;
this.nameStore = (KeyValueStore<String, Name>) context.getStateStore("nameStore");
this.stateStore = (KeyValueStore<String, String>) context.getStateStore("stateStore");
}
#Override
public void process(String key, IncomingEvent value) {
String correlationId = UUID.randomUUID().toString();
try {
String externalDeviceId = value.getExternalDeviceId();
Name nameFromStore = nameStore.get(externalDeviceId);
}
}
}
In nameFromStore variable, I don't get even value even after storing it in NameProcessor.

Passing data to CustomView via method

I've created a custom view that extends LinearLayoutCompat. The view works fine if I hard code the data variables within the constructor, however, I'm wanting to pass data to this view from various different fragments, however I get null object errors when I do this.
fragment_profile:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
...
<com.topper.topper.ui.profile.ProfileSummaryCardView
android:id="#+id/friendsummaryclasstest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:orientation="vertical" />
...
<data>
<variable
name="ProfileViewModel"
type="com.topper.topper.ui.profile.ProfileViewModel" />
</data>
</layout>
ProfileFragment:
public class ProfileFragment extends Fragment {
private ProfileViewModel mViewModel;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
FragmentProfileBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_profile, container, false);
mViewModel = ViewModelProviders.of(this).get(ProfileViewModel.class);
binding.setProfileViewModel(mViewModel);
binding.getProfileViewModel();
return binding.getRoot();
}
#Override
public void onViewCreated(#NotNull View view, #Nullable Bundle savedInstanceState) {
ProfileSummaryCardView friendSummaryCard = view.findViewById(R.id.friendsummaryclasstest);
friendSummaryCard.setParameters(getString(R.string.title_friends), mViewModel.getFriendNames(), mViewModel.getFriendPics());
}
}
ProfileSummaryCardView (CustomView):
public class ProfileSummaryCardView extends LinearLayoutCompat {
private String cardTitle;
private ArrayList<String> imgURLs;
private String[] names;
private Context mContext;
public ProfileSummaryCardView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
final ViewProfileSummaryCardBinding binding = ViewProfileSummaryCardBinding.inflate(LayoutInflater.from(mContext), this, true);
if (imgURLs.size() == 0) {
binding.profileCard.setVisibility(View.GONE);
} else if (imgURLs.size() <= 4) {
binding.profileCardGridrowb.setVisibility(View.GONE);
}
binding.profileCardTitle.setText(cardTitle);
ImageView[] imageViewList = {binding.profileCardIImg,
binding.profileCardIiImg,
binding.profileCardIiiImg,
binding.profileCardIvImg,
binding.profileCardVImg,
binding.profileCardViImg,
binding.profileCardViiImg,
binding.profileCardViiImg};
TextView[] textViewList = {binding.profileCardIName,
binding.profileCardIiName,
binding.profileCardIiiName,
binding.profileCardIvName,
binding.profileCardVName,
binding.profileCardViName,
binding.profileCardViiName,
binding.profileCardViiName};
for (int i = 0; i < imgURLs.size(); i++) {
gridImageSetup(imageViewList[i], imgURLs.get(i));
textViewList[i].setText(names[i]);
}
}
public void setParameters(String pTitle, String[] pNameArray, ArrayList pPicURLArray) {
this.cardTitle = pTitle;
this.imgURLs = pPicURLArray;
this.names = pNameArray;
invalidate();
}
private void gridImageSetup(ImageView target, String url) {
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.transform(new FitCenter(), new RoundedCorners(25))
.into(target);
}
}
It seems like the customview is initialized before the setParameters method call I make in my fragment as the data objects are null when I try and run the above.
Any help would be greatly appreciated.
So, I figured out the fix for this issue and it's was a face-palm moment as it was a really simple fix.
I cut and pasted everything in the constructor of the custom view (except the inflate) into the setParameters method, as below:
public class ProfileSummaryCardView extends LinearLayoutCompat {
private String cardTitle;
private ArrayList<String> imgURLs;
private String[] names;
private Context mContext;
public ProfileSummaryCardView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
final ViewProfileSummaryCardBinding binding = ViewProfileSummaryCardBinding.inflate(LayoutInflater.from(mContext), this, true);
}
public void setParameters(String pTitle, String[] pNameArray, ArrayList pPicURLArray) {
this.cardTitle = pTitle;
this.imgURLs = pPicURLArray;
this.names = pNameArray;
if (imgURLs.size() == 0) {
binding.profileCard.setVisibility(View.GONE);
} else if (imgURLs.size() <= 4) {
binding.profileCardGridrowb.setVisibility(View.GONE);
}
binding.profileCardTitle.setText(cardTitle);
ImageView[] imageViewList = {binding.profileCardIImg,
binding.profileCardIiImg,
binding.profileCardIiiImg,
binding.profileCardIvImg,
binding.profileCardVImg,
binding.profileCardViImg,
binding.profileCardViiImg,
binding.profileCardViiImg};
TextView[] textViewList = {binding.profileCardIName,
binding.profileCardIiName,
binding.profileCardIiiName,
binding.profileCardIvName,
binding.profileCardVName,
binding.profileCardViName,
binding.profileCardViiName,
binding.profileCardViiName};
for (int i = 0; i < imgURLs.size(); i++) {
gridImageSetup(imageViewList[i], imgURLs.get(i));
textViewList[i].setText(names[i]);
}
invalidate();
}
private void gridImageSetup(ImageView target, String url) {
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.transform(new FitCenter(), new RoundedCorners(25))
.into(target);
}
}

JavaFx application Window dimension changes in El Capitan after bundling

I am creating a javaFx application that runs in Mac OSX El Capitan. When I create a bundle from my jar file to .app file, the application window dimension changes.
I want window dimension to be the same before and after bundling. Any help is appreciated.
As I known JavaFX doesn't save/restore window position/dimension automatically.
So, you should restore window position/dimension before show window and save then before window hide. For example I am using such helper:
import javafx.stage.Stage;
import java.util.prefs.Preferences;
public class StagePositionManager {
public static final String IS_FIRST_RUN_KEY = "isFirstRun";
public static final String X_KEY = "x";
public static final String Y_KEY = "y";
public static final String WIDTH_KEY = "width";
public static final String HEIGHT_KEY = "height";
private final Stage stage;
private final Class<?> windowClass;
public StagePositionManager(Stage stage, Class<?> windowClass) {
this.stage = stage;
this.windowClass = windowClass;
restoreStagePosition();
stage.setOnHidden(event -> saveStagePosition());
}
private void saveStagePosition() {
Preferences preferences = Preferences.userNodeForPackage(windowClass);
preferences.putBoolean(IS_FIRST_RUN_KEY, false);
preferences.putDouble(X_KEY, stage.getX());
preferences.putDouble(Y_KEY, stage.getY());
preferences.putDouble(WIDTH_KEY, stage.getWidth());
preferences.putDouble(HEIGHT_KEY, stage.getHeight());
}
private void restoreStagePosition() {
Preferences preferences = Preferences.userNodeForPackage(windowClass);
if (!preferences.getBoolean(IS_FIRST_RUN_KEY, true)) {
stage.setX(preferences.getDouble(X_KEY, 0));
stage.setY(preferences.getDouble(Y_KEY, 0));
stage.setWidth(preferences.getDouble(WIDTH_KEY, 1024));
stage.setHeight(preferences.getDouble(HEIGHT_KEY, 768));
}
}
}
and call it after application start:
public class MyApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
...
new StagePositionManager(primaryStage, Main.class);
...

Why is my recyclerview only displaying the content of the first item?

I am having a problem with my recyclerview, It only displays the content of the first item like this:
I have no idea what caused this, I'm really confused because I have never encountered something like this before. As you can see on the toast, the response return 3 data but I don't understand why the others are not being displayed.
Playlist.java
public class Playlist extends AppCompatActivity {
// inisiasi toolbar
private Toolbar toolbar;
// navigation drawer
public DrawerLayout drawerLayout;
private ActionBarDrawerToggle drawerToggle;
RecyclerView recyclerView;
String[] id,title,dir, artists;
ArrayList<String> artist;
String navTitles[];
TypedArray navIcons;
RecyclerView.Adapter recyclerViewAdapter;
TextView textView;
String video;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playlist);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final Drawable upArrow = getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
upArrow.setColorFilter(getResources().getColor(R.color.colorIcons), PorterDuff.Mode.SRC_ATOP);
getSupportActionBar().setHomeAsUpIndicator(upArrow);
Intent intent = getIntent();
video = intent.getStringExtra("songs");
//textView = (TextView) findViewById(R.id.text);
//textView.setText(video);
getPlaylist();
// dir = PlaylistJson.dirs;
//artist = new ArrayList<String>(Arrays.asList(title));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
finish();
break;
}
return super.onOptionsItemSelected(item);
}
private void getPlaylist(){
final ProgressDialog loading = ProgressDialog.show(this,"Fetching Data","Please wait...",false,false);
//Creating a string request
StringRequest stringRequest = new StringRequest(Request.Method.POST, "http://musicmania.hol.es/playlist/getSongsFromPlaylist",
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//If we are getting success from server
Toast.makeText(Playlist.this, response, Toast.LENGTH_LONG).show();
loading.dismiss();
showPlaylistJSON(response);
id = PlaylistJson.ids;
title = PlaylistJson.titles;
artists = PlaylistJson.artists;
recyclerView= (RecyclerView) findViewById(R.id.my_recycler_view);
RecyclerViewAdapter adapter=new RecyclerViewAdapter(id, title,artists, Playlist.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(Playlist.this));
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//You can handle error here if you want
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
//Adding parameters to request
params.put("playlist", video);
//returning parameter
return params;
}
};
//Adding the string request to the queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
private void showPlaylistJSON(String json){
PlaylistJson pj = new PlaylistJson(json);
pj.parseJSON();
}
}
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
LayoutInflater inflater;
Context context;
String[] id,title, artists;
public RecyclerViewAdapter(String[] id, String[] titles, String[] artists, Context context){
this.id = id;
this.title = titles;
this.artists = artists;
this.context = context;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
RecyclerViewHolder viewHolder = null;
if(Integer.parseInt(id[0]) != 0){
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
viewHolder = new RecyclerViewHolder(view, context);
}else{
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.empty_list, parent, false);
viewHolder = new RecyclerViewHolder(view, context);
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
if(Integer.parseInt(id[0]) != 0) {
holder.item2.setText(title[position]);
holder.imageView2.setTag(holder);
holder.artist.setText(artists[position]);
}else{
holder.item2.setText(title[position]);
}
}
#Override
public int getItemCount() {
return title.length;
}
public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
TextView item;
ImageView imageView;
TextView item2;
TextView artist;
ImageView imageView2;
ImageButton addtoplaylist;
Context context;
public RecyclerViewHolder(final View itemView, final Context context) {
super(itemView);
this.context = context;
item = (TextView) itemView.findViewById(R.id.tv_NavTitle);
imageView = (ImageView) itemView.findViewById(R.id.iv_NavIcon);
item2 = (TextView) itemView.findViewById(R.id.list_title);
imageView2 = (ImageView) itemView.findViewById(R.id.list_avatar);
artist = (TextView) itemView.findViewById(R.id.list_artist);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(), Video.class);
intent.putExtra("video", ParseJson.dirs[getAdapterPosition()]);
v.getContext().startActivity(intent);
}
});
}
}
}
PlaylistJson.java
package com.example.rendell.musicmaniajukebox.json_model;
import android.widget.Toast;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class PlaylistJson {
public static String[] ids;
public static String[] titles;
public static String[] artists;
public static String[] dirs;
public static final String JSON_ARRAY = "result";
public static final String KEY_ID = "id";
public static final String KEY_TITLE = "title";
public static final String KEY_ARTIST = "artist";
public static final String KEY_DIR = "dir";
private JSONArray users = null;
private String json;
public PlaylistJson(String json){
this.json = json;
}
public void parseJSON(){
JSONObject jsonObject=null;
try {
jsonObject = new JSONObject(json);
users = jsonObject.getJSONArray(JSON_ARRAY);
ids = new String[users.length()];
titles = new String[users.length()];
artists = new String[users.length()];
dirs = new String[users.length()];
for(int i=0;i<users.length();i++){
JSONObject jo = users.getJSONObject(i);
ids[i] = jo.getString(KEY_ID);
titles[i] = jo.getString(KEY_TITLE);
artists[i] = jo.getString(KEY_ARTIST);
dirs[i] = jo.getString(KEY_DIR);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
So the problem was in my PlaylistJson.java file. My volley response only returns 3 items per set e.g. {"id":1, "title": "song", "artist":"artist"} but I am also initialing for the dir which doesn't receive any json so maybe the bug came from that. Anyway, removed that and it worked.

GWT application crashes in latest Firefox versions 21 and above

We have a GWT application which crashes in Firefox versions 21 and above, including in the latest version 23.0.1. In earlier versions of Firefox and IE 9, it works fine. This is in deployed mode and not because of the GWT plugin. The situation it crashes is when there are huge number of RPC calls, may be around 300 to 400.
As the application in which it happens is fairly complex, I tried to simulate this issue with a simple prototype. I observed that my prototype crashes when the number of RPC calls reach 100000. But this scenario is very unlikely in my application where RPC calls are around 300-400 as observed using Firebug.
I am trying to find out what else I am missing in my prototype so that it also crashes with 300-400 RPC calls.
GWT version - 2.4
GXT version - 2.2.5
package com.ganesh.check.firefox.client;
public class FirefoxCrash implements EntryPoint {
private static final String SERVER_ERROR = "An error occurred while "
+ "attempting to contact the server. Please check your network "
+ "connection and try again.";
private final GreetingServiceAsync greetingService = GWT
.create(GreetingService.class);
public native static void consoleLog(String text)/*-{
$wnd.console.log(text);
}-*/;
public void onModuleLoad() {
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
nameField.setText("GWT User");
final Label errorLabel = new Label();
final Label countLabel = new Label();
// We can add style names to widgets
sendButton.addStyleName("sendButton");
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(nameField);
RootPanel.get("sendButtonContainer").add(sendButton);
RootPanel.get("errorLabelContainer").add(errorLabel);
RootPanel.get("count").add(countLabel);
// Focus the cursor on the name field when the app loads
nameField.setFocus(true);
nameField.selectAll();
// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);
// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
sendButton.setEnabled(true);
sendButton.setFocus(true);
}
});
class MyHandler implements ClickHandler, KeyUpHandler {
private int resultCount = 0;
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
sendNameToServer();
}
public void onKeyUp(KeyUpEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
sendNameToServer();
}
}
private void sendNameToServer() {
// First, we validate the input.
errorLabel.setText("");
String textToServer = nameField.getText();
// Then, we send the input to the server.
textToServerLabel.setText(textToServer);
serverResponseLabel.setText("");
final int loopCount = Integer.parseInt(textToServer);
resultCount=0;
for (int i = 0; i < loopCount; i++) {
greetingService.getResult(textToServer,
new AsyncCallback<ResultBean>() {
public void onFailure(Throwable caught) {
consoleLog(caught.getMessage());
}
public void onSuccess(ResultBean result) {
//countLabel.setText(++resultCount + "");
resultCount++;
if(resultCount==loopCount){
countLabel.setText(resultCount + "");
}
consoleLog("Result returned for "+resultCount);
}
});
}
}
}
// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
}
}
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
public ResultBean getResult(String name) {
ResultBean result = new ResultBean();
Random random = new Random();
int suffix = random.nextInt();
result.setName("Name "+suffix);
result.setAddress("Address "+suffix);
result.setZipCode(suffix);
result.setDoorNumber("Door "+suffix);
return result;
}
public class ResultBean implements Serializable {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getZipCode() {
return zipCode;
}
public void setZipCode(int zipCode) {
this.zipCode = zipCode;
}
public String getDoorNumber() {
return doorNumber;
}
public void setDoorNumber(String doorNumber) {
this.doorNumber = doorNumber;
}
private String name;
private String address;
private int zipCode;
private String doorNumber;
}

Resources