Why can't the Java compiler find my Hilt class in launchFragmentInHiltContainer? - java

I'm trying to implement the custom launchFragmentInHiltContainer() method in a Java project, and I've already gone through all the hoops of setting up kotlin and refactoring the reified parameters. However, when I try to compile the project, I am greeted by this puzzling error:
C:\Users\jedwa\AndroidStudioProjects\AppName\app\src\androidTest\java\com\example\appname\MainActivityTest.java:55: error: cannot access Hilt_FragmentPersonalDetails
HiltExtKt.launchFragmentInHiltContainer(FragmentPersonalDetails.class);
^
class file for com.example.appname.fragments.Hilt_FragmentPersonalDetails not found
FragmentPersonalDetails is a hilt-enabled fragment and works fine in production code. What is strange is that replacing FragmentPersonalDetails.class with a NonHiltFragment.class will allow the project to compile just fine.
What it will not do, however is stop a runtime error from occurring, which may be related. On replacing FragmentPersonalDetails with NonHiltFragment, I get:
java.lang.RuntimeException: Hilt test, MainActivityTest, is missing generated file: com.example.appname.MainActivityTest_TestComponentDataSupplier. Check that the test class is annotated with #HiltAndroidTest and that the processor is running over your test.
which I have seen before, except this time I most definitely do have #HiltAndroidTest on my test class. I've finally hit the point where the error is internal enough that I have no idea how to go about fixing it, though it seems like a dependency error of some sort. Files provided below for reference.
The hilt-enabled FragmentPersonalDetails
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import com.example.atease.R;
import com.example.atease.databinding.FragmentPersonalDetailsBinding;
import com.example.atease.viewmodels.LoginViewModel;
import dagger.hilt.android.AndroidEntryPoint;
#AndroidEntryPoint
public class FragmentPersonalDetails extends Fragment {
private FragmentPersonalDetailsBinding binding;
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentPersonalDetailsBinding.inflate(inflater, container, false);
binding.setLifecycleOwner(this);
NavController navController = NavHostFragment.findNavController(this);
ViewModelStoreOwner store = navController.getViewModelStoreOwner(R.id.login_graph);
binding.setViewModel(new ViewModelProvider(store).get(LoginViewModel.class));
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.nextButton.setOnClickListener(view1 ->
NavHostFragment.findNavController(FragmentPersonalDetails.this)
.navigate(R.id.action_FragmentPersonalDetails_to_FragmentEmploymentDetails));
}
#Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
The test class that won't compile
import androidx.test.espresso.IdlingRegistry;
import androidx.test.espresso.accessibility.AccessibilityChecks;
import com.example.atease.fragments.SecondFragment;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import javax.inject.Inject;
import dagger.hilt.android.testing.HiltAndroidRule;
import dagger.hilt.android.testing.HiltAndroidTest;
/**
* Instrumented test, which will execute on an Android device.
*
* #see Testing documentation
*/
#HiltAndroidTest
public class MainActivityTest {
#Inject DataBindingIdlingResource bindingIdlingResource;
#BeforeClass
public static void enableAccessibility() {
AccessibilityChecks.enable().setRunChecksFromRootView(true);
}
#Before
public void init() {
hiltRule.inject();
IdlingRegistry.getInstance().register(bindingIdlingResource);
}
#After
public void tearDown() {
IdlingRegistry.getInstance().unregister(bindingIdlingResource);
}
#Rule
public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
//cycles through the nav-bar
#Test
public void testNavbar() {
HiltExtKt.launchFragmentInHiltContainer(SecondFragment.class);
}
}
My implementation of launchFragmentinHiltContainer. The only difference is that I took out the reified parameter types and added the extra class parameter, in order to be able to reference the methods from Java.
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import androidx.annotation.StyleRes
import androidx.core.util.Preconditions
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
#JvmOverloads
#ExperimentalCoroutinesApi
inline fun <T : Fragment> launchFragmentInHiltContainer(
fragmentType: Class<T>,
fragmentArgs: Bundle? = null,
#StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
crossinline action: Fragment.() -> Unit = {}
) {
val startActivityIntent = Intent.makeMainActivity(
ComponentName(
ApplicationProvider.getApplicationContext(),
HiltTestActivity::class.java
)
).putExtra("androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY",
themeResId)
ActivityScenario.launch<HiltTestActivity>(startActivityIntent).onActivity { activity ->
val fragment: Fragment = activity.supportFragmentManager.fragmentFactory.instantiate(
Preconditions.checkNotNull(fragmentType.classLoader),
fragmentType.name
)
fragment.arguments = fragmentArgs
activity.supportFragmentManager
.beginTransaction()
.add(android.R.id.content, fragment, "")
.commitNow()
fragment.action()
}
}
#JvmOverloads
#ExperimentalCoroutinesApi
inline fun <T : Fragment> launchFragmentInHiltContainer(
fragmentType: Class<T>,
fragmentArgs: Bundle? = null,
#StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
factory: FragmentFactory,
crossinline action: Fragment.() -> Unit = {}
) {
val startActivityIntent = Intent.makeMainActivity(
ComponentName(
ApplicationProvider.getApplicationContext(),
HiltTestActivity::class.java
)
).putExtra("androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY",
themeResId)
ActivityScenario.launch<HiltTestActivity>(startActivityIntent).onActivity { activity ->
activity.supportFragmentManager.fragmentFactory = factory
val fragment: Fragment = activity.supportFragmentManager.fragmentFactory.instantiate(
Preconditions.checkNotNull(fragmentType.classLoader),
fragmentType.name
)
fragment.arguments = fragmentArgs
activity.supportFragmentManager
.beginTransaction()
.add(android.R.id.content, fragment, "")
.commit()
fragment.action()
}
}

Just rewrite the function in Java. I changed the signature to match the original FragmentScenario.launchInContainer.
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import java.util.Objects;
public class HiltHelper {
public static void launchFragmentInHiltContainer(Class<? extends Fragment> fragmentClass, Bundle fragmentArgs, #StyleRes int themeResId) {
Intent intent = Intent.makeMainActivity(new ComponentName(ApplicationProvider.getApplicationContext(), HiltTestActivity.class))
.putExtra("androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY", themeResId);
ActivityScenario.launch(intent)
.onActivity(activity -> {
Fragment fragment = ((AppCompatActivity) activity).getSupportFragmentManager().getFragmentFactory()
.instantiate(Objects.requireNonNull(fragmentClass.getClassLoader()), fragmentClass.getName());
fragment.setArguments(fragmentArgs);
((AppCompatActivity) activity).getSupportFragmentManager()
.beginTransaction()
.add(android.R.id.content, fragment, "")
.commitNow();
});
}
}

Related

fragment binding (symbol "id" cannot be resolved)

I am trying to use the binding method in android studio to connect two fragments using onCreate and onViewCreated methods. so far i am getting id not resolved error. I have already connected the fragments on the xml graph. Bellow is the code of the settings java file.
package com.mqtt.workactiv.ui.settings;
import static android.os.Build.VERSION_CODES.R;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import com.mqtt.workactiv.databinding.FragmentSettingsBinding;
public class SettingsFragment extends Fragment {
private FragmentSettingsBinding binding;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
SettingsViewModel settingsViewModel =
new ViewModelProvider(this).get(SettingsViewModel.class);
binding = FragmentSettingsBinding.inflate(inflater, container, false);
View root = binding.getRoot();
return root;
}
#Override
public void onViewCreated (#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
NavController navController = Navigation.findNavController(view);
Button gateConnButton;
gateConnButton = view.findViewById(R.id.gatewayConnButton);
gateConnButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
navController.navigate(R.id.action_navigation_setting_to_gateway4);
}
});
}
}
Make sure to add this in build.gradle(app)
buildFeatures {
viewBinding = true
}
Then give sync project with gradle files.
If this didnt work, try rebuild, invalidate cache/ restart.
Also view.findviewbyid not required if view binding is enabled.
You can access button directly by something like this,
binding.gateConnButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
navController.navigate(R.id.action_navigation_setting_to_gateway4);
}
});
You can try code like this.
NavDirections navDirections = SettingsFragmentDirections.actionNavigationSettingToGateway4();
findNavController().navigate(navDirections, null);
I don't see your navigation xml file, so SettingsFragmentDirections.actionNavigationSettingToGateway4() can have another name. Fix those names yourself.
P.S. the versions of libraries which I'm using androidx.navigation:navigation-fragment-ktx:2.4.0, "androidx.navigation:navigation-ui-ktx:2.4.0"

Disable screenshots in React Native

I know that you can not 100% stop the user from taking a screenshot if he insists to. But I read that you can still stop manual screenshots by setting LayoutParams.FLAG_SECURE in Java.
I tried adding it to my MainApplication file but getWindow() kept on throwing errors no matter what I do. So I moved that line of code to the MainActivity file and it worked without any errors.
Problem is, I can still normally take screenshots.
MainApplication:
package com.testapp;
import android.app.Activity;
import com.reactnativenavigation.NavigationApplication;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import android.support.annotation.Nullable;
/* custom modules */
import com.oblador.vectoricons.VectorIconsPackage;
import org.pgsqlite.SQLitePluginPackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends NavigationApplication {
#Override
public boolean isDebug() {
return BuildConfig.DEBUG;
}
#Nullable
#Override
public List<ReactPackage> createAdditionalReactPackages() {
return Arrays.<ReactPackage>asList(
new SQLitePluginPackage(),
new VectorIconsPackage(),
new RNDeviceInfo()
);
}
#Override
public void onCreate() {
super.onCreate();
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
sharedI18nUtilInstance.allowRTL(getApplicationContext(), false);
}
}
MainActivity:
package com.testapp;
import android.widget.ImageView;
import com.reactnativenavigation.controllers.SplashActivity;
import android.os.Bundle;
import android.view.WindowManager.LayoutParams;
public class MainActivity extends SplashActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
}
}
I did simply the following and it is working properly:
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);
setContentView(R.layout.activity_main);
}
}

android java How to override toString of ParseUser in nested class

In Android, I am trying to override the toString() of the ParseUser for use in my listview, using
import android.os.Bundle;
import android.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.parse.FindCallback;
import com.parse.ParseException;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import java.util.List;
/**
* A simple {#link Fragment} subclass.
*/
public class ExampleFragment extends Fragment {
public ExampleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_example, container, false);
final ListView listView = (ListView) rootView.findViewById(R.id.exampleListView);
ParseQuery query = User.getQuery();
query.findInBackground(new FindCallback<User>() {
public void done(List<User> objects, ParseException e) {
if (e == null) {
// The query was successful.
listView.setAdapter(new ArrayAdapter<User>(rootView.getContext(), android.R.layout.simple_list_item_1, objects));
for (User user : objects) {
Log.i("AppInfo", "Added " + user.toString());
}
} else {
// Something went wrong.
}
}
});
return rootView;
}
public class User extends ParseUser {
#Override
public String toString() {
Log.i("AppInfo", "Username: " + getUsername());
return getUsername();
}
}
}
Seems simple enough. I had this nested in my fragment class and it crashed with this
java.lang.ClassCastException: com.parse.ParseUser cannot be cast to com.mycompany.myproject.ExampleFragment$User
but when I move it into its own subclass (where it gets its own entry in the explorer view in android studio), it works perfectly.
Is there a reason it does not work nested in my fragment class? Seems a bit unnecessary to create its own subclass just to override one function.
UPDATE: If I remove the for loop part it does not error out, but it still does not call my overridden toString() as the listview returns com.parse.ParseUser#etcetcetc.
make sure you have do these 2 things
1.register class before Parse.initialize
ParseObject.registerSubclass(User.class);
2.use annotation to specified the class mapping to waht Class
#ParseClassName("_User")
public class User extends ParseUser {
...
}
here is the reference.
https://parse.com/docs/android/guide#objects-subclassing-parseobject

java.lang.ClassNotFoundException: com.hamweather.aeris.maps.R$layout using Aeris Weather SDK

I am using the Hamweather Aeris Andorid SDK, and I am trying to implement the map view component. When I follow their online tutorials, I can not render the AerisMapView, and I get the error:
java.lang.ClassNotFoundException: com.hamweather.aeris.maps.R$layout.
Does anyone know where this is coming from/how to fix it?
My xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.hamweather.aeris.maps.AerisMapView
android:id="#+id/aerisfragment_map"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
</com.hamweather.aeris.maps.AerisMapView>
</LinearLayout>
And my Activity:
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
public class MapViewActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview_activity);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MapFragment fragment = new MapFragment();
fragmentTransaction.add(R.id.frame_container, fragment);
fragmentTransaction.commit();
}
}
Finally, the fragment:
import android.location.*;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.model.LatLng;
import com.hamweather.aeris.communication.AerisCallback;
import com.hamweather.aeris.communication.EndpointType;
import com.hamweather.aeris.location.LocationHelper;
import com.hamweather.aeris.maps.AerisMapView;
import com.hamweather.aeris.maps.AerisMapView.AerisMapType;
import com.hamweather.aeris.maps.MapViewFragment;
import com.hamweather.aeris.maps.interfaces.OnAerisMapLongClickListener;
import com.hamweather.aeris.maps.interfaces.OnAerisMarkerInfoWindowClickListener;
import com.hamweather.aeris.maps.markers.AerisMarker;
import com.hamweather.aeris.model.AerisResponse;
import com.hamweather.aeris.response.EarthquakesResponse;
import com.hamweather.aeris.response.FiresResponse;
import com.hamweather.aeris.response.StormCellResponse;
import com.hamweather.aeris.response.StormReportsResponse;
public class MapFragment extends MapViewFragment implements OnAerisMapLongClickListener, AerisCallback,
OnAerisMarkerInfoWindowClickListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.single_tab_site_weather2,
container, false);
mapView = (AerisMapView) view.findViewById(R.id.aerisfragment_map);
mapView.init(savedInstanceState, AerisMapType.GOOGLE);
initMap();
setHasOptionsMenu(true);
return view;
}
/**
* Inits the map with specific setting
*/
private void initMap() {
mapView.moveToLocation(new LatLng(34.7, -86.7), 9);
mapView.setOnAerisMapLongClickListener(this);
mapView.setOnAerisWindowClickListener(this);
}
#Override
public void onResult(EndpointType endpointType, AerisResponse aerisResponse) {
}
#Override
public void onMapLongClick(double v, double v1) {
}
#Override
public void wildfireWindowPressed(FiresResponse firesResponse, AerisMarker aerisMarker) {
}
#Override
public void stormCellsWindowPressed(StormCellResponse stormCellResponse, AerisMarker aerisMarker) {
}
#Override
public void stormReportsWindowPressed(StormReportsResponse stormReportsResponse, AerisMarker aerisMarker) {
}
#Override
public void earthquakeWindowPressed(EarthquakesResponse earthquakesResponse, AerisMarker aerisMarker) {
}
}
Also, this is my first Q on stack exchange, so if I failed to adhere to a certain convention or etiquette, please let me know, and I'll try to fix it. Thanks.
I Managed to get it working. For anyone who has this issue, here were my steps:
I first switched from using the jars to using the gradle dependency. Per their website, add the following to your build.gradle(the module one)
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.android.gms:play-services:4.4.52'
compile 'com.hamweather:aeris-maps-library:1.0.0#aar'
}
Also be sure to add the following, in the andorid tag, which probably already exists in the file:
android {
...some other stuff...
dexOptions{
preDexLibraries = false
}
}
Double check that you have the proper API key permissions for google maps. Then it works. As far as I can tell, it had something to do with using the compiled jar, not the repo version.

Enum's working with .setText in android/java

I am building a Android app in Java, use a Enum variable in a .setText scenario for a XML TextView, i fully understand my Enum is in the wrong data type, but i need your help to work out a alternative to this.
The code where i'm using the setText:
package com.mastermind;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
public class Guess_database extends Activity {
public ArrayList<Guess> guess_list;
public static int guess_counter = 0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.guess_layout);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Guess g : guess_list) {
View stub = inflater.inflate(R.layout.guess_stub, null);
((TextView) stub.findViewById(R.id.guess)).setText(g.v1 + ", "+ g.v2);
((TextView)stub.findViewById(R.id.guess_positions)).setText(g.c1);
}
}
}
And here is the class for my Enum variable:
package com.mastermind;
import java.io.Serializable;
public class Guess implements Serializable
{
static int v1, v2, v3, v4;
public static GuessStatus c1,c2, c3, c4;
enum GuessStatus
{
CORRECT,
WRONG,
CORRECT_WRONG_PLACE;
}
}
Any help will be appreciated.
((TextView)stub.findViewById(R.id.guess_positions)).setText(g.c1.toString());

Categories