diff --git a/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/RequestExecutorTest.java b/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/RequestExecutorTest.java index e351bb1b..4a74e999 100644 --- a/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/RequestExecutorTest.java +++ b/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/RequestExecutorTest.java @@ -19,6 +19,7 @@ import net.yupol.transmissionremote.app.server.Server; import net.yupol.transmissionremote.app.transport.request.Request; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,10 +50,11 @@ public class RequestExecutorTest { @Mock private RequestListener mockListener; @Captor private ArgumentCaptor spiceExceptionArgumentCaptor; + private AutoCloseable closeable; @Before public void setup() throws Exception { - MockitoAnnotations.initMocks(this); + closeable = MockitoAnnotations.openMocks(this); when(mockRequest.getResultType()) .thenReturn(String.class); @@ -200,5 +202,9 @@ static RequestListener wrap(@Nonnull RequestListener listener, @Nonnul return new CountDownRequestListenerWrapper<>(listener, latch); } } - //endregion + + @After + public void tearDown() throws Exception { + closeable.close(); + } } diff --git a/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/SpiceTransportManagerTest.java b/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/SpiceTransportManagerTest.java index f94282a6..0765591e 100644 --- a/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/SpiceTransportManagerTest.java +++ b/app/src/androidTest/java/net/yupol/transmissionremote/app/transport/SpiceTransportManagerTest.java @@ -16,6 +16,7 @@ import net.yupol.transmissionremote.app.server.Server; import net.yupol.transmissionremote.app.transport.request.Request; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,7 +28,7 @@ public class SpiceTransportManagerTest { private static final String SESSION_ID = "fake_session_id"; private static final String REDIRECT_LOCATION = "fake_redirect_location"; - + private AutoCloseable closeable; private Server server; @Mock private Request request; @@ -35,7 +36,7 @@ public class SpiceTransportManagerTest { @Before public void setup() { - MockitoAnnotations.initMocks(this); + closeable = MockitoAnnotations.openMocks(this); server = new Server("Test Server", "http://localhost", 9091); } @@ -89,4 +90,9 @@ public void execute(SpiceRequest request, RequestListener requestListe verifyNoMoreInteractions(requestListener); assertThat(server.getRedirectLocation()).isEqualTo(REDIRECT_LOCATION); } + + @After + public void tearDown() throws Exception { + closeable.close(); + } } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/MainActivity.java b/app/src/main/java/net/yupol/transmissionremote/app/MainActivity.java index 9db8f7cd..1d7d1b3c 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/MainActivity.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/MainActivity.java @@ -1,9 +1,8 @@ package net.yupol.transmissionremote.app; -import static android.preference.PreferenceManager.getDefaultSharedPreferences; - import android.Manifest; import android.annotation.SuppressLint; +import android.app.Activity; import android.app.AlertDialog; import android.app.SearchManager; import android.content.ActivityNotFoundException; @@ -14,10 +13,8 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.preference.PreferenceManager; import android.provider.MediaStore; import android.provider.OpenableColumns; import android.util.Log; @@ -95,6 +92,8 @@ import net.yupol.transmissionremote.app.transport.request.StartTorrentRequest; import net.yupol.transmissionremote.app.transport.request.StopTorrentRequest; import net.yupol.transmissionremote.app.transport.request.TorrentRemoveRequest; +import net.yupol.transmissionremote.app.utils.TorrentLoader; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; @@ -106,7 +105,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.util.Collection; import java.util.List; import java.util.Timer; @@ -214,6 +212,8 @@ public void onRequestSuccess(AddTorrentResult addTorrentResult) { private boolean showFab; private FreeSpaceFooterDrawerItem freeSpaceFooterDrawerItem; private FinishedTorrentsNotificationManager finishedTorrentsNotificationManager; + private ActivityResultLauncher chooseTorrentLauncher; + private ActivityResultLauncher addServerLauncher; private final ActivityResultLauncher requestPermissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestPermission(), @@ -245,6 +245,41 @@ protected void onCreate(Bundle savedInstanceState) { openTorrentUri = savedInstanceState.getParcelable(KEY_OPEN_TORRENT_URI); openTorrentScheme = savedInstanceState.getString(KEY_OPEN_TORRENT_SCHEME); } + + chooseTorrentLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + Intent data = result.getData(); + if (data != null) { + Uri uri = data.getData(); + try { + InputStream inputStream = getContentResolver().openInputStream(uri); + openTorrentByLocalFile(inputStream); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + } + } + ); + + addServerLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + Intent data = result.getData(); + if (data != null) { + Server server = data.getParcelableExtra(AddServerActivity.EXTRA_SEVER); + addNewServer(server); + switchServer(server); + if (application.getServers().size() == 1 && application.isNotificationEnabled()) { + requestNotificationsPermissionIfRequired(); + } + } + } + } + ); } private void logAppStartupTime() { @@ -559,7 +594,7 @@ private void openTorrentFileByUriWithScheme(Uri uri, String scheme) { protected void onResume() { super.onResume(); isActivityResumed = true; - getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); + TransmissionRemotePreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); List servers = application.getServers(); if (servers.isEmpty()) { @@ -576,7 +611,7 @@ protected void onResume() { binding.addTorrentButton.collapseImmediately(); - showFab = PreferenceManager.getDefaultSharedPreferences(this) + showFab = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(this) .getBoolean(getString(R.string.show_add_torrent_fab_key), true); boolean isListVisible = getTorrentListFragment() != null; binding.addTorrentButton.setVisibility(showFab && isListVisible ? View.VISIBLE : View.GONE); @@ -602,7 +637,7 @@ protected void onPause() { stopPreferencesUpdateTimer(); - getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); + TransmissionRemotePreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); application.persist(); } @@ -770,20 +805,9 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_SERVER_PARAMS) { - if (resultCode == RESULT_OK) { - Server server = data.getParcelableExtra(AddServerActivity.EXTRA_SEVER); - addNewServer(server); - switchServer(server); - if (application.getServers().size() == 1 && application.isNotificationEnabled()) { - requestNotificationsPermissionIfRequired(); - } - } - } else if (requestCode == REQUEST_CODE_CHOOSE_TORRENT) { - if (resultCode == RESULT_OK) { - openTorrentUri = data.getData(); - openTorrentUriOnResume = true; - } + if (requestCode == REQUEST_CODE_CHOOSE_TORRENT && resultCode == RESULT_OK) { + openTorrentUri = data.getData(); + openTorrentUriOnResume = true; } } @@ -942,22 +966,28 @@ public void onDownloadLocationSelected(Bundle args, final String downloadDir, fi new AddTorrentByFileRequest(args.getByteArray(DownloadLocationDialogFragment.KEY_FILE_BYTES), downloadDir, !startWhenAdded), addTorrentResultListener); break; + case DownloadLocationDialogFragment.REQUEST_CODE_BY_REMOTE_FILE: Uri fileUri = args.getParcelable(DownloadLocationDialogFragment.KEY_FILE_URI); - new RetrieveTorrentContentAsyncTask() { + TorrentLoader.loadTorrentFromUri(fileUri, new TorrentLoader.Callback() { @Override - protected void onPostExecute(byte[] bytes) { - if (bytes != null) { - TransportManager tm = getTransportManager(); - if (tm.isStarted()) { - tm.doRequest(new AddTorrentByFileRequest(bytes, downloadDir, !startWhenAdded), addTorrentResultListener); - } - } else { - Toast.makeText(MainActivity.this, getString(R.string.error_cannot_read_file_msg), Toast.LENGTH_SHORT).show(); + public void onSuccess(byte[] data) { + TransportManager tm = getTransportManager(); + if (tm.isStarted()) { + tm.doRequest(new AddTorrentByFileRequest(data, downloadDir, !startWhenAdded), addTorrentResultListener); } } - }.execute(fileUri); + + @Override + public void onError() { + Toast.makeText(MainActivity.this, + getString(R.string.error_cannot_read_file_msg), + Toast.LENGTH_SHORT + ).show(); + } + }); break; + case DownloadLocationDialogFragment.REQUEST_CODE_BY_MAGNET: String magnetUri = args.getString(DownloadLocationDialogFragment.KEY_MAGNET_URI); if (magnetUri != null) { @@ -984,7 +1014,7 @@ public void onRemoveTorrentsSelected(int[] torrentsToRemove, boolean removeData) public void openAddServerActivity(View view) { Intent intent = new Intent(this, AddServerActivity.class); - startActivityForResult(intent, REQUEST_CODE_SERVER_PARAMS); + addServerLauncher.launch(intent); } private void addNewServer(Server server) { @@ -1153,9 +1183,8 @@ private void showFileChooser() { intent.addCategory(Intent.CATEGORY_OPENABLE); try { - startActivityForResult( - Intent.createChooser(intent, getResources().getString(R.string.select_torrent_file)), - MainActivity.REQUEST_CODE_CHOOSE_TORRENT); + Intent chooser = Intent.createChooser(intent, getResources().getString(R.string.select_torrent_file)); + chooseTorrentLauncher.launch(chooser); } catch (ActivityNotFoundException ex) { Toast.makeText(this, getResources().getString(R.string.error_install_file_manager_msg), @@ -1211,26 +1240,4 @@ private void pauseAllTorrents() { getTransportManager().doRequest(new StopTorrentRequest(application.getTorrents()), null); torrentUpdater.scheduleUpdate(UPDATE_REQUEST_DELAY); } - - private static abstract class RetrieveTorrentContentAsyncTask extends AsyncTask { - - @Override - protected byte[] doInBackground(Uri... torrentFileUris) { - String uri = torrentFileUris[0].toString(); - InputStream inputStream = null; - try { - inputStream = new URL(uri).openConnection().getInputStream(); - return IOUtils.toByteArray(inputStream); - } catch (IOException e) { - Log.e(TAG, "Failed to retrieve Uri '" + uri + "'", e); - } finally { - if (inputStream != null) try { - inputStream.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close InputStream", e); - } - } - return null; - } - } } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/actionbar/ActionBarNavigationAdapter.java b/app/src/main/java/net/yupol/transmissionremote/app/actionbar/ActionBarNavigationAdapter.java index 9fb61d20..89a62d71 100755 --- a/app/src/main/java/net/yupol/transmissionremote/app/actionbar/ActionBarNavigationAdapter.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/actionbar/ActionBarNavigationAdapter.java @@ -38,7 +38,7 @@ public ActionBarNavigationAdapter(Context context) { textColorPrimary = ColorUtils.resolveColor(context, android.R.attr.textColorPrimary, R.color.text_primary); textColorPrimaryInverse = ColorUtils.resolveColor(context, android.R.attr.textColorPrimaryInverse, R.color.text_primary_inverse); accentColor = ColorUtils.resolveColor(context, R.attr.colorAccent, R.color.accent); - alternativeAccentColor = context.getResources().getColor(R.color.alternative_accent); + alternativeAccentColor = context.getResources().getColor(R.color.alternative_accent, null); } @Override diff --git a/app/src/main/java/net/yupol/transmissionremote/app/actionbar/SpeedTextView.java b/app/src/main/java/net/yupol/transmissionremote/app/actionbar/SpeedTextView.java index fcdb67ac..d2c7b826 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/actionbar/SpeedTextView.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/actionbar/SpeedTextView.java @@ -27,7 +27,7 @@ public SpeedTextView(Context context, @DrawableRes int iconRes, @ColorRes int ic setPadding(horPadding, 0, horPadding, 0); setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.speed_text_size)); - setTextColor(context.getResources().getColor(R.color.text_primary_inverse)); + setTextColor(context.getResources().getColor(R.color.text_primary_inverse, null)); setTypeface(null, Typeface.BOLD); setSpeed(0); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/drawer/HeaderView.java b/app/src/main/java/net/yupol/transmissionremote/app/drawer/HeaderView.java index 9efb9c36..a19ad252 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/drawer/HeaderView.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/drawer/HeaderView.java @@ -2,7 +2,6 @@ import android.content.Context; import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -23,6 +22,7 @@ import net.yupol.transmissionremote.app.TransmissionRemote; import net.yupol.transmissionremote.app.server.Server; import net.yupol.transmissionremote.app.utils.ColorUtils; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; import org.json.JSONArray; import org.json.JSONException; @@ -149,7 +149,7 @@ public void setServers(List servers, int currentServerPosition) { } TransmissionRemote app = TransmissionRemote.getApplication(getContext()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences sp = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(getContext()); List orderedServers; if (sp.contains(KEY_ORDERED_SERVERS)) { orderedServers = serversFromJson(sp.getString(KEY_ORDERED_SERVERS, "")); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/notifications/BackgroundUpdater.java b/app/src/main/java/net/yupol/transmissionremote/app/notifications/BackgroundUpdater.java index 121e3184..69848a48 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/notifications/BackgroundUpdater.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/notifications/BackgroundUpdater.java @@ -1,14 +1,14 @@ package net.yupol.transmissionremote.app.notifications; import android.content.Context; -import android.preference.PreferenceManager; import net.yupol.transmissionremote.app.R; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; public class BackgroundUpdater { public static void start(Context context) { - boolean onlyUnmeteredNetwork = PreferenceManager.getDefaultSharedPreferences(context) + boolean onlyUnmeteredNetwork = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(context) .getBoolean(context.getString(R.string.background_update_only_unmetered_wifi_key), true); BackgroundUpdateJob.schedule(onlyUnmeteredNetwork); } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesActivity.java b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesActivity.java index fbd426d9..40daf14b 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesActivity.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesActivity.java @@ -1,12 +1,13 @@ package net.yupol.transmissionremote.app.preferences; import android.os.Bundle; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.app.ActionBar; import android.util.Log; import android.view.MenuItem; +import androidx.appcompat.app.ActionBar; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + import com.octo.android.robospice.persistence.exception.SpiceException; import com.octo.android.robospice.request.listener.RequestListener; @@ -96,7 +97,7 @@ public void onBackPressed() { SessionSetRequest.Builder requestBuilder = fragment.getPreferencesRequestBuilder(); if (requestBuilder.isChanged()) { saveChangesRequest = requestBuilder.build(); - new SaveChangesDialogFragment().show(getFragmentManager(), TAG_SAVE_CHANGES_DIALOG); + new SaveChangesDialogFragment().show(getSupportFragmentManager(), TAG_SAVE_CHANGES_DIALOG); } else { super.onBackPressed(); } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesFragment.java index 71b154cd..c1a090cb 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServerPreferencesFragment.java @@ -15,7 +15,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.MenuHost; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; +import androidx.lifecycle.Lifecycle; import com.octo.android.robospice.persistence.exception.SpiceException; import com.octo.android.robospice.request.listener.RequestListener; @@ -45,7 +48,6 @@ public class ServerPreferencesFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); } @Override @@ -81,6 +83,27 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat serverSettings = savedInstanceState.getParcelable(KEY_SERVER_SETTINGS); } updateUi(); + + MenuHost menuHost = requireActivity(); + menuHost.addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.server_preferences_menu, menu); + ServerPreferencesFragment.this.menu = menu; // Save menu reference + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_save) { + SessionSetRequest.Builder builder = getPreferencesRequestBuilder(); + if (builder.isChanged()) { + sendUpdateOptionsRequest(builder.build()); + } + return true; + } + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); } @Override @@ -89,25 +112,6 @@ public void onSaveInstanceState(@NonNull Bundle outState) { outState.putParcelable(KEY_SERVER_SETTINGS, serverSettings); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - this.menu = menu; - inflater.inflate(R.menu.server_preferences_menu, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_save: - SessionSetRequest.Builder builder = getPreferencesRequestBuilder(); - if (builder.isChanged()) { - sendUpdateOptionsRequest(builder.build()); - } - return true; - } - return false; - } - public SessionSetRequest.Builder getPreferencesRequestBuilder() { SessionSetRequest.Builder builder = SessionSetRequest.builder(); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersActivity.java b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersActivity.java index d0fa2fb1..43a6fd70 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersActivity.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersActivity.java @@ -1,16 +1,20 @@ package net.yupol.transmissionremote.app.preferences; +import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.app.ActionBar; import android.util.Log; import android.view.MenuItem; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.ActionBar; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + import net.yupol.transmissionremote.app.BaseActivity; import net.yupol.transmissionremote.app.R; import net.yupol.transmissionremote.app.TransmissionRemote; @@ -30,6 +34,7 @@ public class ServersActivity extends BaseActivity { public static final String KEY_SERVER_UUID = "key_server_uuid"; private TransmissionRemote app; + private ActivityResultLauncher addServerLauncher; @Override protected void onCreate(Bundle savedInstanceState) { @@ -75,6 +80,20 @@ public void onBackStackChanged() { invalidateOptionsMenu(); } }); + + addServerLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + Intent data = result.getData(); + if (data != null) { + Server server = data.getParcelableExtra(AddServerActivity.EXTRA_SEVER); + app.addServer(server); + app.setActiveServer(server); + } + } + } + ); } private void showServerDetails(Server server) { @@ -101,7 +120,7 @@ private void showServerDetails(Server server, boolean saveBackStack) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_add: - startActivityForResult(new Intent(this, AddServerActivity.class), REQUEST_CODE_NEW_SERVER); + addServerLauncher.launch(new Intent(this, AddServerActivity.class)); return true; case R.id.action_remove: new AlertDialog.Builder(this) @@ -154,14 +173,4 @@ public boolean onSupportNavigateUp() { } } } - - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_NEW_SERVER) { - if (resultCode == RESULT_OK) { - Server server = data.getParcelableExtra(AddServerActivity.EXTRA_SEVER); - app.addServer(server); - app.setActiveServer(server); - } - } - } } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersFragment.java index a7325b3c..76378760 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/preferences/ServersFragment.java @@ -5,6 +5,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; @@ -13,7 +14,10 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.view.MenuHost; +import androidx.core.view.MenuProvider; import androidx.fragment.app.ListFragment; +import androidx.lifecycle.Lifecycle; import net.yupol.transmissionremote.app.R; import net.yupol.transmissionremote.app.TransmissionRemote; @@ -76,13 +80,26 @@ public View getView(int position, View convertView, ViewGroup parent) { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); } @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setEmptyText(getString(R.string.servers_empty_text)); + + MenuHost menuHost = requireActivity(); + menuHost.addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.servers_menu, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + // Handle menu item selection here if needed + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); } @Override @@ -97,11 +114,6 @@ public void onAttach(@NonNull Context context) { app = (TransmissionRemote) requireActivity().getApplication(); } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.servers_menu, menu); - } - @Override public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) { BaseAdapter adapter = (BaseAdapter) getListAdapter(); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/server/ServerDetailsFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/server/ServerDetailsFragment.java index 0667723c..1801922f 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/server/ServerDetailsFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/server/ServerDetailsFragment.java @@ -10,6 +10,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -21,7 +22,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.view.MenuHost; +import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; +import androidx.lifecycle.Lifecycle; import com.google.api.client.repackaged.com.google.common.base.Strings; @@ -57,7 +61,6 @@ public class ServerDetailsFragment extends Fragment implements OnBackPressedList @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(getServerArgument() != null); } @Override @@ -144,11 +147,21 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat isAuthEnabled = savedInstanceState.getBoolean(KEY_IS_AUTH_ENABLED); updateAuth(isAuthEnabled); } - } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.server_details_menu, menu); + MenuHost menuHost = requireActivity(); + menuHost.addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + if (getServerArgument() != null) { + menuInflater.inflate(R.menu.server_details_menu, menu); + } + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); } @Override diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/BandwidthLimitFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/BandwidthLimitFragment.java index a3112ed9..67384f6f 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/BandwidthLimitFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/BandwidthLimitFragment.java @@ -1,9 +1,9 @@ package net.yupol.transmissionremote.app.torrentdetails; import android.app.Activity; +import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; -import androidx.fragment.app.Fragment; import android.text.InputFilter; import android.util.AttributeSet; import android.util.Log; @@ -15,6 +15,10 @@ import android.widget.EditText; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + import net.yupol.transmissionremote.app.R; public class BandwidthLimitFragment extends Fragment implements View.OnFocusChangeListener { @@ -36,9 +40,9 @@ public class BandwidthLimitFragment extends Fragment implements View.OnFocusChan private boolean disableableLimits; @Override - public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) { - super.onInflate(activity, attrs, savedInstanceState); - + public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs, @Nullable Bundle savedInstanceState) { + super.onInflate(context, attrs, savedInstanceState); + Activity activity = (Activity) context; TypedArray a = activity.obtainStyledAttributes(attrs, R.styleable.BandwidthLimitFragment); disableableLimits = a.getBoolean(R.styleable.BandwidthLimitFragment_disableable_limits, true); a.recycle(); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/DirectoryAdapter.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/DirectoryAdapter.java index e263a2aa..92d3edb3 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/DirectoryAdapter.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/DirectoryAdapter.java @@ -287,7 +287,7 @@ public ViewHolder(final FileItemBinding binding, final OnItemSelectedListener li @Override public void onClick(View v) { if (getItemViewType() == R.id.view_type_directory) { - listener.onDirectorySelected(getAdapterPosition()); + listener.onDirectorySelected(getBindingAdapterPosition()); } else { if (binding.checkbox.isEnabled()) { binding.checkbox.setChecked(!binding.checkbox.isChecked()); @@ -299,12 +299,12 @@ public void onClick(View v) { @Override public void onStateChanged(IndeterminateCheckBox buttonView, @Nullable Boolean isChecked) { if (getItemViewType() == R.id.view_type_directory) { - boolean changed = isChecked != isDirectoryChecked(getDir(getAdapterPosition())); - if (isChecked != null && changed) listener.onDirectoryChecked(getAdapterPosition(), isChecked); + boolean changed = isChecked != isDirectoryChecked(getDir(getBindingAdapterPosition())); + if (isChecked != null && changed) listener.onDirectoryChecked(getBindingAdapterPosition(), isChecked); } else { - Integer fileIndex = getItem(getAdapterPosition()); + Integer fileIndex = getItem(getBindingAdapterPosition()); assert isChecked != null; - boolean changed = isChecked != isFileChecked(getAdapterPosition()); + boolean changed = isChecked != isFileChecked(getBindingAdapterPosition()); if (changed) { listener.onFileChecked(fileIndex, isChecked); } @@ -323,7 +323,7 @@ public void onClick(View view) { public void onItemClick(AdapterView parent, View view, int priorityPosition, long id) { Priority priority = (Priority) parent.getItemAtPosition(priorityPosition); - int position = getAdapterPosition(); + int position = getBindingAdapterPosition(); if (getItemViewType() == R.id.view_type_directory) { listener.onDirectoryPriorityChanged(position, priority); setDirPriority(getDir(position), priority); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/FilesPageFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/FilesPageFragment.java index d3d1446f..9cb1bbff 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/FilesPageFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/FilesPageFragment.java @@ -31,7 +31,7 @@ public class FilesPageFragment extends BasePageFragment implements DirectoryFrag private static final String TAG_PROGRESSBAR_FRAGMENT = "tag_progressbar_fragment"; private static final String TAG_DIRECTORY_FRAGMENT = "tag_directory_fragment"; private static final String KEY_PATH = "key_path"; - + private boolean isVisibleToUser = false; private boolean viewCreated; private final Stack path = new Stack<>(); private BreadcrumbView breadcrumbView; @@ -75,6 +75,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, @Override public void onResume() { super.onResume(); + isVisibleToUser = true; TransmissionRemote.getInstance().getAnalytics().logScreenView( "Files page", FilesPageFragment.class @@ -102,6 +103,11 @@ public void onSaveInstanceState(@NonNull Bundle outState) { outState.putStringArray(KEY_PATH, pathNames); } + @Override + public void onPause() { + super.onPause(); + isVisibleToUser = false; + } @Override public void setTorrentInfo(TorrentInfo torrentInfo) { @@ -176,7 +182,7 @@ public void onDirectorySelected(Dir dir) { @Override public boolean onBackPressed() { - if (getUserVisibleHint() && path.size() > 1) { + if (isVisibleToUser && path.size() > 1) { path.pop(); breadcrumbView.setPath(path); showDirectory(path.peek(), AnimationDirection.LEFT_TO_RIGHT); @@ -227,4 +233,4 @@ private enum AnimationDirection { this.exit = exit; } } -} +} \ No newline at end of file diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/OptionsPageFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/OptionsPageFragment.java index a5a3ff0f..6f2d5be5 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/OptionsPageFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/OptionsPageFragment.java @@ -15,7 +15,10 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.MenuProvider; import androidx.databinding.DataBindingUtil; +import androidx.lifecycle.Lifecycle; import com.octo.android.robospice.persistence.exception.SpiceException; import com.octo.android.robospice.request.listener.RequestListener; @@ -86,8 +89,6 @@ public void onDetach() { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - sessionGetRequest = new SessionGetRequest(); transportManager.doRequest(sessionGetRequest, new RequestListener<>() { @Override @@ -144,6 +145,33 @@ public void run() { return binding.getRoot(); } + @Override + public void onViewCreated(@NonNull View view,@Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // Add MenuProvider + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.torrent_options_menu, menu); + saveMenuItem = menu.findItem(R.id.action_save); + saveMenuItem.setEnabled(getTorrentInfo() != null); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == R.id.action_save) { + TorrentSetRequest.Builder requestBuilder = getSaveOptionsRequestBuilder(); + if (requestBuilder.isChanged()) { + sendUpdateOptionsRequest(requestBuilder.build()); + } + return true; + } + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); + } + @Override public void onResume() { super.onResume(); @@ -291,27 +319,6 @@ private int getIdleLimit() { } } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.torrent_options_menu, menu); - saveMenuItem = menu.findItem(R.id.action_save); - saveMenuItem.setEnabled(getTorrentInfo() != null); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_save: - TorrentSetRequest.Builder requestBuilder = getSaveOptionsRequestBuilder(); - if (requestBuilder.isChanged()) { - sendUpdateOptionsRequest(requestBuilder.build()); - } - return true; - } - - return false; - } - @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { updateSeedingLimitsUi(false); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/PeersPageFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/PeersPageFragment.java index 5d4e2ac6..1deb3f69 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/PeersPageFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/PeersPageFragment.java @@ -2,7 +2,6 @@ import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -14,7 +13,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.ListPopupWindow; +import androidx.core.view.MenuHost; +import androidx.core.view.MenuProvider; import androidx.databinding.DataBindingUtil; +import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -28,6 +30,7 @@ import net.yupol.transmissionremote.app.utils.DividerItemDecoration; import net.yupol.transmissionremote.app.utils.MetricsUtils; import net.yupol.transmissionremote.app.utils.Size; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; public class PeersPageFragment extends BasePageFragment { @@ -42,8 +45,7 @@ public class PeersPageFragment extends BasePageFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + preferences = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(getContext()); } @Override @@ -75,6 +77,28 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, return binding.getRoot(); } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + MenuHost menuHost = requireActivity(); + menuHost.addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.torrent_peers_menu, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_sort_peers) { + showSortingList(); + return true; + } + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); + } + @Override public void onResume() { super.onResume(); @@ -90,21 +114,6 @@ public void onDestroyView() { viewCreated = false; } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.torrent_peers_menu, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_sort_peers: - showSortingList(); - return true; - } - return super.onOptionsItemSelected(item); - } - private void showSortingList() { ListPopupWindow popup = new ListPopupWindow(requireContext()); popup.setModal(true); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/SaveChangesDialogFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/SaveChangesDialogFragment.java index 4cf257b3..7c8f9fe3 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/SaveChangesDialogFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/SaveChangesDialogFragment.java @@ -3,10 +3,11 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.app.DialogFragment; import android.content.DialogInterface; import android.os.Bundle; +import androidx.fragment.app.DialogFragment; + import net.yupol.transmissionremote.app.R; public class SaveChangesDialogFragment extends DialogFragment { diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TorrentDetailsActivity.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TorrentDetailsActivity.java index 20470562..d3089604 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TorrentDetailsActivity.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TorrentDetailsActivity.java @@ -3,7 +3,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.util.Log; import android.util.SparseArray; import android.view.Menu; @@ -38,6 +37,7 @@ import net.yupol.transmissionremote.app.transport.request.TorrentRemoveRequest; import net.yupol.transmissionremote.app.transport.request.TorrentSetRequest; import net.yupol.transmissionremote.app.transport.request.VerifyTorrentRequest; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; import java.util.LinkedList; import java.util.List; @@ -75,7 +75,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.torrent_details_layout); - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(TorrentDetailsActivity.this); + sharedPreferences = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(TorrentDetailsActivity.this); torrent = getIntent().getParcelableExtra(EXTRA_TORRENT); setupPager(); @@ -242,7 +242,7 @@ private boolean handleExit() { } if (saveChangesRequests.size() > 0) { - new SaveChangesDialogFragment().show(getFragmentManager(), TAG_SAVE_CHANGES_DIALOG); + new SaveChangesDialogFragment().show(getSupportFragmentManager(), TAG_SAVE_CHANGES_DIALOG); } else { finish(); return true; diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersAdapter.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersAdapter.java index 0edc6e3a..a7342116 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersAdapter.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersAdapter.java @@ -1,14 +1,15 @@ package net.yupol.transmissionremote.app.torrentdetails; -import androidx.databinding.DataBindingUtil; -import androidx.annotation.NonNull; -import androidx.appcompat.widget.PopupMenu; -import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.PopupMenu; +import androidx.databinding.DataBindingUtil; +import androidx.recyclerview.widget.RecyclerView; + import net.yupol.transmissionremote.app.R; import net.yupol.transmissionremote.app.databinding.TrackerItemLayoutBinding; import net.yupol.transmissionremote.app.model.json.TrackerStats; @@ -96,7 +97,7 @@ public void onClick(View v) { @Override public boolean onMenuItemClick(MenuItem item) { - int position = getAdapterPosition(); + int position = getBindingAdapterPosition(); if (position == RecyclerView.NO_POSITION) return false; switch (item.getItemId()) { diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersPageFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersPageFragment.java index 838144b6..4ff44b18 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersPageFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentdetails/TrackersPageFragment.java @@ -9,7 +9,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -22,9 +21,11 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.ListPopupWindow; +import androidx.core.view.MenuProvider; import androidx.databinding.DataBindingUtil; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -46,6 +47,7 @@ import net.yupol.transmissionremote.app.utils.DividerItemDecoration; import net.yupol.transmissionremote.app.utils.MetricsUtils; import net.yupol.transmissionremote.app.utils.Size; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; import org.apache.commons.lang3.StringUtils; @@ -77,8 +79,7 @@ public void onAttach(@NonNull Context context) { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + preferences = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(getContext()); } @Override @@ -111,6 +112,30 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, return binding.getRoot(); } + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + requireActivity().addMenuProvider(new MenuProvider() { + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.torrent_trackers_menu, menu); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + int itemId = menuItem.getItemId(); + if (itemId == R.id.action_add) { + showEditTrackerUrlDialog(null); + return true; + } else if (itemId == R.id.action_sort_trackers) { + showSortingList(); + return true; + } + return false; + } + }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); + } + @Override public void onResume() { super.onResume(); @@ -126,24 +151,6 @@ public void onDestroyView() { viewCreated = false; } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.torrent_trackers_menu, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_add: - showEditTrackerUrlDialog(null); - return true; - case R.id.action_sort_trackers: - showSortingList(); - return true; - } - return super.onOptionsItemSelected(item); - } - private void showSortingList() { ListPopupWindow popup = new ListPopupWindow(requireContext()); popup.setModal(true); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/ChooseLocationDialogFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/ChooseLocationDialogFragment.java index 6aabb7e7..56436587 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/ChooseLocationDialogFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/ChooseLocationDialogFragment.java @@ -14,7 +14,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.databinding.DataBindingUtil; import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.Fragment; import com.octo.android.robospice.exception.RequestCancelledException; import com.octo.android.robospice.persistence.exception.SpiceException; @@ -34,7 +33,6 @@ public class ChooseLocationDialogFragment extends DialogFragment { public static final String ARG_INITIAL_LOCATION = "arg_initial_location"; - private OnLocationSelectedListener listener; private SetLocationDialogBinding binding; private FreeSpaceRequest runningFreeSpaceRequest; @@ -43,15 +41,9 @@ public class ChooseLocationDialogFragment extends DialogFragment { @Override public void onAttach(Context context) { super.onAttach(context); - - Fragment targetFragment = getTargetFragment(); - if (targetFragment instanceof OnLocationSelectedListener) { - listener = (OnLocationSelectedListener) targetFragment; - } else { - Activity activity = getActivity(); - if (activity instanceof OnLocationSelectedListener) { - listener = (OnLocationSelectedListener) activity; - } + Activity activity = getActivity(); + if (activity instanceof OnLocationSelectedListener) { + listener = (OnLocationSelectedListener) activity; } } @@ -87,13 +79,18 @@ public void afterTextChanged(Editable s) { @Override public void onClick(DialogInterface dialog, int which) { if (listener != null) { - listener.onLocationSelected(binding.locationEdit.getText().toString(), - binding.moveDataCheckbox.isChecked()); + listener.onLocationSelected(binding.locationEdit.getText().toString(), binding.moveDataCheckbox.isChecked()); + return; } + + Bundle result = new Bundle(); + result.putString("selected_location", binding.locationEdit.getText().toString()); + result.putBoolean("move_data_checked", binding.moveDataCheckbox.isChecked()); + getParentFragmentManager().setFragmentResult("location_result", result); + dismiss(); } }); builder.setNegativeButton(android.R.string.cancel, null); - return builder.create(); } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/RemoveTorrentsDialogFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/RemoveTorrentsDialogFragment.java index 8698cb9e..4a9b0d89 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/RemoveTorrentsDialogFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/RemoveTorrentsDialogFragment.java @@ -1,10 +1,11 @@ package net.yupol.transmissionremote.app.torrentlist; -import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; + import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; @@ -52,12 +53,12 @@ public void onClick(DialogInterface dialog, int which) { } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); + public void onAttach(Context context) { + super.onAttach(context); try { - listener = (OnRemoveTorrentSelectionListener) activity; + listener = (OnRemoveTorrentSelectionListener) context; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement " + OnRemoveTorrentSelectionListener.class.getSimpleName()); + throw new ClassCastException(context.toString() + " must implement " + OnRemoveTorrentSelectionListener.class.getSimpleName()); } } diff --git a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/TorrentListFragment.java b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/TorrentListFragment.java index dc0703f4..4e301a69 100644 --- a/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/TorrentListFragment.java +++ b/app/src/main/java/net/yupol/transmissionremote/app/torrentlist/TorrentListFragment.java @@ -5,7 +5,6 @@ import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; -import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; @@ -27,6 +26,7 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentResultListener; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -55,6 +55,7 @@ import net.yupol.transmissionremote.app.utils.ColorUtils; import net.yupol.transmissionremote.app.utils.DividerItemDecoration; import net.yupol.transmissionremote.app.utils.TextUtils; +import net.yupol.transmissionremote.app.utils.TransmissionRemotePreferenceManager; import net.yupol.transmissionremote.app.utils.diff.Equals; import net.yupol.transmissionremote.app.utils.diff.ListDiff; import net.yupol.transmissionremote.app.utils.diff.Range; @@ -286,7 +287,7 @@ public void onStop() { public void onResume() { super.onResume(); - boolean showFab = PreferenceManager.getDefaultSharedPreferences(getContext()) + boolean showFab = TransmissionRemotePreferenceManager.getDefaultSharedPreferences(getContext()) .getBoolean(getString(R.string.show_add_torrent_fab_key), true); recyclerView.setPadding(0, 0, 0, showFab ? getResources().getDimensionPixelOffset(R.dimen.fab_size_normal) : 0); } @@ -392,8 +393,20 @@ private void updateEmptyTextVisibility() { } private void showChooseLocationDialog() { + getParentFragmentManager().setFragmentResultListener( + "location_result", // requestKey + getViewLifecycleOwner(), + new FragmentResultListener() { + @Override + public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle result) { + String selectedLocation = result.getString("selected_location"); + boolean checked = result.getBoolean("move_data_checked"); + onLocationSelected(selectedLocation, checked); + } + } + ); + ChooseLocationDialogFragment dialog = new ChooseLocationDialogFragment(); - dialog.setTargetFragment(this, 0); dialog.show(requireActivity().getSupportFragmentManager(), CHOOSE_LOCATION_FRAGMENT_TAG); } @@ -441,7 +454,7 @@ public void onClick(View v) { } } else { v.setSelected(true); - toggleSelection(viewHolder.getAdapterPosition()); + toggleSelection(viewHolder.getBindingAdapterPosition()); } } }); @@ -454,7 +467,7 @@ public boolean onLongClick(View v) { } actionMode = requireActivity().startActionMode(actionModeCallback); - toggleSelection(viewHolder.getAdapterPosition()); + toggleSelection(viewHolder.getBindingAdapterPosition()); return true; } }); diff --git a/app/src/main/java/net/yupol/transmissionremote/app/utils/TorrentLoader.java b/app/src/main/java/net/yupol/transmissionremote/app/utils/TorrentLoader.java new file mode 100644 index 00000000..11d10bc5 --- /dev/null +++ b/app/src/main/java/net/yupol/transmissionremote/app/utils/TorrentLoader.java @@ -0,0 +1,49 @@ +package net.yupol.transmissionremote.app.utils; + +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class TorrentLoader { + + public interface Callback { + void onSuccess(byte[] data); + void onError(); + } + + public static void loadTorrentFromUri(Uri fileUri, Callback callback) { + if (fileUri == null || callback == null) { + return; + } + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Handler mainHandler = new Handler(Looper.getMainLooper()); + + executor.execute(() -> { + byte[] bytes = null; + try (InputStream inputStream = new URL(fileUri.toString()).openStream()) { + bytes = IOUtils.toByteArray(inputStream); + } catch (IOException e) { + Log.e("TorrentLoader", "Failed to retrieve Uri: " + fileUri, e); + } + + byte[] finalBytes = bytes; + mainHandler.post(() -> { + if (finalBytes != null) { + callback.onSuccess(finalBytes); + } else { + callback.onError(); + } + }); + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/net/yupol/transmissionremote/app/utils/TransmissionRemotePreferenceManager.java b/app/src/main/java/net/yupol/transmissionremote/app/utils/TransmissionRemotePreferenceManager.java new file mode 100644 index 00000000..84c3a412 --- /dev/null +++ b/app/src/main/java/net/yupol/transmissionremote/app/utils/TransmissionRemotePreferenceManager.java @@ -0,0 +1,12 @@ +package net.yupol.transmissionremote.app.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +public class TransmissionRemotePreferenceManager { + private static final String APPLICATION_PREFERENCES = "transmission_remote_app_prefs"; + + public static SharedPreferences getDefaultSharedPreferences(Context context) { + return context.getSharedPreferences(APPLICATION_PREFERENCES, Context.MODE_PRIVATE); + } +} diff --git a/gradle.properties b/gradle.properties index 13e0e5a7..47494304 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,4 @@ android.enableJetifier=true android.useAndroidX=true org.gradle.jvmargs=-Xmx4096m +android.experimental.androidTest.useUnifiedTestPlatform=false \ No newline at end of file