diff --git a/BundleClient/app/src/main/java/net/discdd/bundleclient/BundleClientActivity.java b/BundleClient/app/src/main/java/net/discdd/bundleclient/BundleClientActivity.java index 02da663ae4..895614525f 100644 --- a/BundleClient/app/src/main/java/net/discdd/bundleclient/BundleClientActivity.java +++ b/BundleClient/app/src/main/java/net/discdd/bundleclient/BundleClientActivity.java @@ -1,5 +1,6 @@ package net.discdd.bundleclient; +import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; @@ -10,11 +11,14 @@ import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; @@ -23,6 +27,7 @@ import net.discdd.android.fragments.LogFragment; import net.discdd.android.fragments.PermissionsFragment; +import net.discdd.android.fragments.PermissionsViewModel; import net.discdd.client.bundlerouting.ClientWindow; import net.discdd.client.bundlesecurity.BundleSecurity; import net.discdd.pathutils.ClientPaths; @@ -47,6 +52,14 @@ public class BundleClientActivity extends AppCompatActivity { BundleClientWifiDirectService wifiBgService; private final ServiceConnection connection; CompletableFuture serviceReady = new CompletableFuture<>(); + private PermissionsFragment permissionsFragment; + private BundleClientWifiDirectFragment homeFragment; + private UsbFragment usbFragment; + private ServerFragment serverFragment; + private LogFragment logFragment; + private ViewPager2 viewPager; + private TabLayout tabLayout; + private TabLayoutMediator tabLayoutMediator; public BundleClientActivity() { connection = new ServiceConnection() { @@ -84,27 +97,64 @@ protected void onCreate(Bundle savedInstanceState) { var intent = new Intent(this, BundleClientWifiDirectService.class); var svc = bindService(intent, connection, Context.BIND_AUTO_CREATE); - BundleClientWifiDirectFragment bundleClientWifiDirectFragment = new BundleClientWifiDirectFragment(); - fragmentsWithTitles.add(new FragmentWithTitle(bundleClientWifiDirectFragment, getString(R.string.home_tab))); - var permissionsFragment = new PermissionsFragment(); + PermissionsViewModel permissionsViewModel = new ViewModelProvider(this).get(PermissionsViewModel.class); + permissionsFragment = new PermissionsFragment(permissionsViewModel); + homeFragment = new BundleClientWifiDirectFragment(); + usbFragment = new UsbFragment(); + serverFragment = new ServerFragment(); + logFragment = new LogFragment(); fragmentsWithTitles.add(new FragmentWithTitle(permissionsFragment, getString(R.string.permissions_tab))); - fragmentsWithTitles.add(new FragmentWithTitle(new UsbFragment(), getString(R.string.usb_tab))); - fragmentsWithTitles.add(new FragmentWithTitle(new ServerFragment(), getString(R.string.server_tab))); - fragmentsWithTitles.add(new FragmentWithTitle(new LogFragment(), getString(R.string.logs_tab))); connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); //set up view setContentView(R.layout.activity_bundle_client); //Set up ViewPager and TabLayout - ViewPager2 viewPager = findViewById(R.id.view_pager); - TabLayout tabLayout = findViewById(R.id.tab_layout); + viewPager = findViewById(R.id.view_pager); + tabLayout = findViewById(R.id.tab_layout); ViewPagerAdapter adapter = new ViewPagerAdapter(this); viewPager.setAdapter(adapter); - var tabMediator = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText( + tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText( fragmentsWithTitles.get(position).title())); - tabMediator.attach(); + tabLayoutMediator.attach(); + + //set observer on view model for permissions + permissionsViewModel.getPermissionSatisfied().observe(this, this::updateTabs); + } + + private void updateTabs(Boolean satisfied) { + logger.log(INFO, "UPDATING TABS ... Permissions satisfied: " + satisfied); + + ArrayList newFragments = new ArrayList<>(); + if (satisfied) { + logger.log(INFO, "ALL TABS BEING SHOWN"); + newFragments.add(new FragmentWithTitle(homeFragment, getString(R.string.home_tab))); + newFragments.add(new FragmentWithTitle(usbFragment, getString(R.string.usb_tab))); + newFragments.add(new FragmentWithTitle(serverFragment, getString(R.string.server_tab))); + newFragments.add(new FragmentWithTitle(logFragment, getString(R.string.logs_tab))); + } else { + logger.log(INFO, "ONLY PERMISSIONS TAB IS BEING SHOWN"); + newFragments.add(new FragmentWithTitle(permissionsFragment, getString(R.string.permissions_tab))); + } + + if (!newFragments.equals(fragmentsWithTitles)) { + new Handler(Looper.getMainLooper()).post(() -> { + if (tabLayoutMediator != null) { + tabLayoutMediator.detach(); + } + + fragmentsWithTitles.clear(); + fragmentsWithTitles.addAll(newFragments); + + ViewPagerAdapter adapter = new ViewPagerAdapter(this); + viewPager.setAdapter(adapter); + + tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText( + fragmentsWithTitles.get(position).title())); + tabLayoutMediator.attach(); + }); + } } @Override diff --git a/BundleTransport/app/src/main/java/net/discdd/bundletransport/BundleTransportActivity.java b/BundleTransport/app/src/main/java/net/discdd/bundletransport/BundleTransportActivity.java index 3981bf1d8c..6d2d493081 100644 --- a/BundleTransport/app/src/main/java/net/discdd/bundletransport/BundleTransportActivity.java +++ b/BundleTransport/app/src/main/java/net/discdd/bundletransport/BundleTransportActivity.java @@ -1,5 +1,6 @@ package net.discdd.bundletransport; +import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; @@ -13,11 +14,14 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; @@ -27,6 +31,7 @@ import net.discdd.android.fragments.LogFragment; import net.discdd.android.fragments.PermissionsFragment; import net.discdd.pathutils.TransportPaths; +import net.discdd.android.fragments.PermissionsViewModel; import net.discdd.transport.TransportSecurity; import org.whispersystems.libsignal.InvalidKeyException; @@ -49,13 +54,18 @@ public class BundleTransportActivity extends AppCompatActivity { private TitledFragment storageFragment; private TransportPaths transportPaths; private TitledFragment usbFrag; + private TitledFragment logFragment; + private TitledFragment permissionsTitledFragment; record ConnectivityEvent(boolean internetAvailable) {} private final SubmissionPublisher connectivityEventPublisher = new SubmissionPublisher<>(); private ViewPager2 viewPager2; private FragmentStateAdapter viewPager2Adapter; + private PermissionsViewModel permissionsViewModel; private PermissionsFragment permissionsFragment; + private TabLayout tabLayout; + private TabLayoutMediator mediator; private SharedPreferences sharedPreferences; TransportWifiServiceConnection transportWifiServiceConnection = new TransportWifiServiceConnection(); @@ -102,15 +112,14 @@ protected void onCreate(Bundle savedInstanceState) { new TransportWifiDirectFragment(this.transportPaths)); storageFragment = new TitledFragment("Storage Settings", new StorageFragment()); usbFrag = new TitledFragment("USB", new UsbFragment(transportPaths)); + logFragment = new TitledFragment(getString(R.string.logs), new LogFragment()); - permissionsFragment = new PermissionsFragment(); - fragments.add(serverUploadFragment); - fragments.add(usbFrag); - fragments.add(transportWifiFragment); - fragments.add(storageFragment); - fragments.add(new TitledFragment(getString(R.string.permissions), permissionsFragment)); - fragments.add(new TitledFragment(getString(R.string.logs), new LogFragment())); - TabLayout tabLayout = findViewById(R.id.tabs); + permissionsViewModel = new ViewModelProvider(this).get(PermissionsViewModel.class); + permissionsFragment = new PermissionsFragment(permissionsViewModel); + permissionsTitledFragment = new TitledFragment("Permissions", permissionsFragment); + fragments.add(permissionsTitledFragment); + + tabLayout = findViewById(R.id.tabs); viewPager2 = findViewById(R.id.view_pager); viewPager2Adapter = new FragmentStateAdapter(this) { @NonNull @@ -125,10 +134,59 @@ public int getItemCount() { } }; viewPager2.setAdapter(viewPager2Adapter); - var mediator = new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> { + + mediator = new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> { tab.setText(fragments.get(position).title); }); mediator.attach(); + + //set observer on view model for permissions + permissionsViewModel.getPermissionSatisfied().observe(this, this::updateTabs); + } + + private void updateTabs(Boolean satisfied) { + logger.log(INFO, "UPDATING TABS ... Permissions satisfied: " + satisfied); + + ArrayList newFragments = new ArrayList<>(); + if (satisfied) { + logger.log(INFO, "ALL TABS BEING SHOWN"); + newFragments.add(serverUploadFragment); + newFragments.add(transportWifiFragment); + newFragments.add(storageFragment); + newFragments.add(logFragment); + } else { + logger.log(INFO, "ONLY PERMISSIONS TAB IS BEING SHOWN"); + newFragments.add(permissionsTitledFragment); + } + + if (!newFragments.equals(fragments)) { + new Handler(Looper.getMainLooper()).post(() -> { + if (mediator != null) { + mediator.detach(); + } + + fragments.clear(); + fragments.addAll(newFragments); + + viewPager2Adapter = new FragmentStateAdapter(this) { + @NonNull + @Override + public Fragment createFragment(int position) { + return fragments.get(position).fragment; + } + + @Override + public int getItemCount() { + return fragments.size(); + } + }; + viewPager2.setAdapter(viewPager2Adapter); + + mediator = new TabLayoutMediator(tabLayout, viewPager2, + (tab, position) -> tab.setText(fragments.get(position).title())); + mediator.attach(); + }); + } } protected void onStart() { diff --git a/android-core/src/main/java/net/discdd/android/fragments/PermissionsFragment.java b/android-core/src/main/java/net/discdd/android/fragments/PermissionsFragment.java index 718a04b421..f85f566130 100644 --- a/android-core/src/main/java/net/discdd/android/fragments/PermissionsFragment.java +++ b/android-core/src/main/java/net/discdd/android/fragments/PermissionsFragment.java @@ -32,9 +32,14 @@ public class PermissionsFragment extends Fragment { final static private Logger logger = Logger.getLogger(PermissionsFragment.class.getName()); private final HashMap neededPermissions = new HashMap<>(); private ActivityResultLauncher requestPermissionLauncher; - private PermissionsAdapter permissionsAdapter; private boolean promptPending; private Consumer> permissionWatcher; + private final PermissionsViewModel permissionsViewModel; + private final HashSet requiredPermissions = new HashSet<>(); + + public PermissionsFragment(PermissionsViewModel permissionsViewModel) { + this.permissionsViewModel = permissionsViewModel; + } @Override public View onCreateView( @@ -53,15 +58,23 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat //get permissions array from resources String[] permissions = getResources().getStringArray(R.array.permissions_array); + //Store required permissions + requiredPermissions.addAll(Arrays.asList(permissions)); + //Initialize the adapter - permissionsAdapter = new PermissionsAdapter(permissions); + PermissionsAdapter permissionsAdapter = new PermissionsAdapter(permissions); //set the adapter to the RecyclerView permissionsRecyclerView.setAdapter(permissionsAdapter); - } } + public void allSatisfied() { + boolean satisfied = !requiredPermissions.isEmpty() && grantedPermissions.containsAll(requiredPermissions); + logger.log(Level.INFO, "ALL PERMS SATISFIED: " + satisfied); + permissionsViewModel.updatePermissions(satisfied); + } + private ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback>() { @@ -86,6 +99,7 @@ public void onActivityResult(Map results) { logger.info("Requesting " + Arrays.toString(permissionsToRequest)); activityResultLauncher.launch(permissionsToRequest); } + allSatisfied(); } }); ; @@ -122,6 +136,7 @@ public void checkPermission(String permission, PermissionsAdapter.PermissionView activityResultLauncher.launch(permissionsToRequest); } } + allSatisfied(); } class PermissionsAdapter extends RecyclerView.Adapter { diff --git a/android-core/src/main/java/net/discdd/android/fragments/PermissionsViewModel.java b/android-core/src/main/java/net/discdd/android/fragments/PermissionsViewModel.java new file mode 100644 index 0000000000..2178c0041d --- /dev/null +++ b/android-core/src/main/java/net/discdd/android/fragments/PermissionsViewModel.java @@ -0,0 +1,17 @@ +package net.discdd.android.fragments; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class PermissionsViewModel extends ViewModel { + private final MutableLiveData permissionSatisfied = new MutableLiveData(); + + public LiveData getPermissionSatisfied() { + return permissionSatisfied; + } + + public void updatePermissions(boolean newPermissionSatisfied) { + permissionSatisfied.setValue(newPermissionSatisfied); + } +}