diff --git a/README.md b/README.md index f22712a4..f61b3336 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Please respect the coding standards for easier merging. master contains the curr Code structure ============== -Starting with version 2.3.0, Transdroid is developed in Android Studio, fully integrating with the Gradle build system. It is compiled against Android 4.4 (API level 19) and since version 2.2.0 supporting ICS (API level 15) and up only. To support lite (Transdrone, specially for the Play Store) and full (Transdroid) versions of the app, build flavours are defined in gradle, which contain version-specific resources. Dependencies are managed via Maven Central in the app's build.gradle file. +Starting with version 2.3.0, Transdroid is developed in Android Studio, fully integrating with the Gradle build system. It is (since version 2.5.0) compiled against Android 5.1 (API level 22) and (since version 2.2.0) supporting ICS (API level 15) and up only. To support lite (Transdrone, specially for the Play Store) and full (Transdroid) versions of the app, build flavours are defined in gradle, which contain version-specific resources. Dependencies are managed via JCentral in the app's build.gradle file. Developed By ============ diff --git a/app/build.gradle b/app/build.gradle index 695d67fe..040cff37 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,29 +1,19 @@ apply plugin: 'com.android.application' apply plugin: 'android-apt' -apply from: '../signing.gradle' android { - compileSdkVersion 19 - buildToolsVersion '20.0.0' + compileSdkVersion 22 + buildToolsVersion '21.1.2' defaultConfig { minSdkVersion 15 - targetSdkVersion 19 - versionCode 217 - versionName '2.3.0' - } - signingConfigs { - release { - storeFile STORE_FILE - storePassword STORE_PASSWORD - keyAlias KEY_ALIAS - keyPassword KEY_PASSWORD - } + targetSdkVersion 22 + versionCode 221 + versionName '2.5.1' } buildTypes { release { - runProguard false - signingConfig signingConfigs.release + minifyEnabled false } } productFlavors { @@ -43,19 +33,21 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'org.androidannotations:androidannotations-api:3.1' + compile 'org.androidannotations:androidannotations-api:3.2' compile 'com.j256.ormlite:ormlite-core:4.48' compile 'com.j256.ormlite:ormlite-android:4.48' - compile 'com.github.chrisbanes.actionbarpulltorefresh:library:0.8' - compile 'de.keyboardsurfer.android.widget:crouton:1.8.+' - compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.+' - compile 'com.android.support:support-annotations:20.0.0' - apt "org.androidannotations:androidannotations:3.1" + compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3' + compile 'com.android.support:appcompat-v7:22.1.1' + compile 'com.android.support:support-annotations:22.1.1' + compile 'com.getbase:floatingactionbutton:1.8.0' + compile 'com.afollestad:material-dialogs:0.7.6.0' + compile 'com.nispok:snackbar:2.10.6' + apt 'org.androidannotations:androidannotations:3.2' } apt { arguments { - androidManifestFile variant.processResources.manifestFile + androidManifestFile variant.outputs[0].processResources.manifestFile resourcePackageName 'org.transdroid' //logLevel 'INFO' //logFile '/Users/erickok/Dev/transdroid/transdroid/app/build/aa-log.txt' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f24b2c4..88ed9c9e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,7 +44,7 @@ android:hardwareAccelerated="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" - android:theme="@android:style/Theme.Holo" > + android:theme="@style/Theme.AppCompat" > + android:windowSoftInputMode="stateHidden" > @@ -194,7 +194,7 @@ - + files, Priority priority) { - DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, - new ArrayList(files)).execute(log); + DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, new ArrayList<>(files)).execute(log); if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset)); } else { @@ -353,12 +323,11 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @UiThread protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) { // Set the activity result so the calling activity knows it needs to update its view - setResult(RESULT_OK, - new Intent().putExtra("torrent_updated", true).putExtra("affected_torrent", torrent)); + setResult(RESULT_OK, new Intent().putExtra("torrent_updated", true).putExtra("affected_torrent", torrent)); // Refresh the screen as well refreshTorrent(); refreshTorrentDetails(torrent); - Crouton.showText(this, successMessage, NavigationHelper.CROUTON_INFO_STYLE); + SnackbarManager.show(Snackbar.with(this).text(successMessage)); } @UiThread @@ -370,7 +339,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @UiThread protected void onTorrentFilesRetrieved(Torrent torrent, List torrentFiles) { // Update the details fragment with the newly retrieved list of files - fragmentDetails.updateTorrentFiles(torrent, new ArrayList(torrentFiles)); + fragmentDetails.updateTorrentFiles(torrent, new ArrayList<>(torrentFiles)); } @UiThread @@ -378,8 +347,8 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R log.i(this, result.getException().toString()); String error = getString(LocalTorrent.getResourceForDaemonException(result.getException())); fragmentDetails.updateIsLoading(false, isCritical ? error : null); - Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), - NavigationHelper.CROUTON_ERROR_STYLE); + SnackbarManager.show(Snackbar.with(this).text(getString(LocalTorrent.getResourceForDaemonException(result.getException()))) + .colorResource(R.color.red)); } @UiThread @@ -387,8 +356,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R // Update the details fragment accordingly fragmentDetails.updateIsLoading(false, null); fragmentDetails.perhapsUpdateTorrent(torrents); - fragmentDetails.updateLabels(Label.convertToNavigationLabels(labels, - getResources().getString(R.string.labels_unlabeled))); + fragmentDetails.updateLabels(Label.convertToNavigationLabels(labels, getResources().getString(R.string.labels_unlabeled))); } } diff --git a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java index 93860d8a..66e220b2 100644 --- a/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java +++ b/app/src/main/java/org/transdroid/core/gui/DetailsFragment.java @@ -16,10 +16,28 @@ */ package org.transdroid.core.gui; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import android.annotation.SuppressLint; +import android.app.Fragment; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.ActionMenuView; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AbsListView.MultiChoiceModeListener; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.nispok.snackbar.Snackbar; +import com.nispok.snackbar.SnackbarManager; +import com.nispok.snackbar.enums.SnackbarType; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Click; @@ -27,13 +45,17 @@ import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.ItemClick; import org.androidannotations.annotations.OptionsItem; -import org.androidannotations.annotations.OptionsMenu; import org.androidannotations.annotations.ViewById; import org.transdroid.R; -import org.transdroid.core.app.settings.*; +import org.transdroid.core.app.settings.ServerSetting; +import org.transdroid.core.app.settings.SystemSettings_; import org.transdroid.core.gui.lists.DetailsAdapter; import org.transdroid.core.gui.lists.SimpleListItemAdapter; -import org.transdroid.core.gui.navigation.*; +import org.transdroid.core.gui.navigation.Label; +import org.transdroid.core.gui.navigation.NavigationHelper_; +import org.transdroid.core.gui.navigation.RefreshableActivity; +import org.transdroid.core.gui.navigation.SelectionManagerMode; +import org.transdroid.core.gui.navigation.SetLabelDialog; import org.transdroid.core.gui.navigation.SetLabelDialog.OnLabelPickedListener; import org.transdroid.core.gui.navigation.SetStorageLocationDialog; import org.transdroid.core.gui.navigation.SetStorageLocationDialog.OnStorageLocationUpdatedListener; @@ -45,33 +67,19 @@ import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentDetails; import org.transdroid.daemon.TorrentFile; -import android.annotation.SuppressLint; -import android.app.Fragment; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AbsListView.MultiChoiceModeListener; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.TextView; -import de.keyboardsurfer.android.widget.crouton.Crouton; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** - * Fragment that shows detailed statistics about some torrent. These come from some already fetched {@link Torrent} - * object, but it also retrieves further detailed statistics. The actual execution of tasks is performed by the activity - * that contains this fragment, as per the {@link TorrentTasksExecutor} interface. + * Fragment that shows detailed statistics about some torrent. These come from some already fetched {@link Torrent} object, but it also retrieves + * further detailed statistics. The actual execution of tasks is performed by the activity that contains this fragment, as per the {@link + * TorrentTasksExecutor} interface. * @author Eric Kok */ -@EFragment(resName = "fragment_details") -@OptionsMenu(resName = "fragment_details") -public class DetailsFragment extends Fragment implements OnTrackersUpdatedListener, OnLabelPickedListener, - OnStorageLocationUpdatedListener { +@EFragment(R.layout.fragment_details) +public class DetailsFragment extends Fragment implements OnTrackersUpdatedListener, OnLabelPickedListener, OnStorageLocationUpdatedListener { // Local data @InstanceState @@ -91,9 +99,15 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen private ServerSetting currentServerSettings = null; // Views - @ViewById(resName = "details_container") + @ViewById protected View detailsContainer; - @ViewById(resName = "details_list") + @ViewById(R.id.details_menu) + protected ActionMenuView detailsMenu; + @ViewById(R.id.contextual_menu) + protected ActionMenuView contextualMenu; + @ViewById + protected SwipeRefreshLayout swipeRefreshLayout; + @ViewById protected ListView detailsList; @ViewById protected TextView emptyText, errorText; @@ -103,6 +117,9 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @AfterViews protected void init() { + // Inject menu options in the actions toolbar + setHasOptionsMenu(true); + // On large screens where this fragment is shown next to the torrents list, we show a continues grey vertical // line to separate the lists visually if (!NavigationHelper_.getInstance_(getActivity()).isSmallScreen()) { @@ -113,22 +130,33 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen } } + createMenuOptions(); + // Set up details adapter (itself containing the actual lists to show), which allows multi-select and fast // scrolling detailsList.setAdapter(new DetailsAdapter(getActivity())); detailsList.setMultiChoiceModeListener(onDetailsSelected); detailsList.setFastScrollEnabled(true); if (getActivity() != null && getActivity() instanceof RefreshableActivity) { - ((RefreshableActivity) getActivity()).addRefreshableView(detailsList); + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + ((RefreshableActivity) getActivity()).refreshScreen(); + swipeRefreshLayout.setRefreshing(false); // Use our custom indicator + } + }); } // Restore the fragment state (on orientation changes et al.) - if (torrent != null) + if (torrent != null) { updateTorrent(torrent); - if (torrentDetails != null) + } + if (torrentDetails != null) { updateTorrentDetails(torrent, torrentDetails); - if (torrentFiles != null) + } + if (torrentFiles != null) { updateTorrentFiles(torrent, torrentFiles); + } } @@ -151,7 +179,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen errorText.setVisibility(View.GONE); loadingProgress.setVisibility(View.GONE); // Also update the available actions in the action bar - getActivity().invalidateOptionsMenu(); + updateMenuOptions(); // Refresh the detailed statistics (errors) and list of files torrentDetails = null; torrentFiles = null; @@ -166,13 +194,14 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen */ public void updateTorrentDetails(Torrent checkTorrent, TorrentDetails newTorrentDetails) { // Check if these are actually the details of the torrent we are now showing - if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID())) + if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID())) { return; + } this.torrentDetails = newTorrentDetails; - ((DetailsAdapter) detailsList.getAdapter()).updateTrackers( - SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getTrackers())); - ((DetailsAdapter) detailsList.getAdapter()).updateErrors( - SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getErrors())); + ((DetailsAdapter) detailsList.getAdapter()) + .updateTrackers(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getTrackers())); + ((DetailsAdapter) detailsList.getAdapter()) + .updateErrors(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getErrors())); } /** @@ -182,22 +211,23 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen */ public void updateTorrentFiles(Torrent checkTorrent, ArrayList newTorrentFiles) { // Check if these are actually the details of the torrent we are now showing - if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID())) + if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID())) { return; + } Collections.sort(newTorrentFiles); this.torrentFiles = newTorrentFiles; ((DetailsAdapter) detailsList.getAdapter()).updateTorrentFiles(newTorrentFiles); } /** - * Can be called if some outside activity returned new torrents, so we can perhaps piggyback on this by update our - * data as well. + * Can be called if some outside activity returned new torrents, so we can perhaps piggyback on this by update our data as well. * @param torrents The last of retrieved torrents */ public void perhapsUpdateTorrent(List torrents) { // Only try to update if we actually were showing a torrent - if (this.torrentId == null || torrents == null) + if (this.torrentId == null || torrents == null) { return; + } for (Torrent newTorrent : torrents) { if (newTorrent.getUniqueID().equals(torrentId)) { // Found, so we can update our data as well @@ -208,12 +238,12 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen } /** - * Updates the locally maintained list of labels that are active on the server. Used in the label picking dialog and - * should be updated every time after the list of torrents was retrieved to keep it updated. + * Updates the locally maintained list of labels that are active on the server. Used in the label picking dialog and should be updated every time + * after the list of torrents was retrieved to keep it updated. * @param currentLabels The list of known server labels */ public void updateLabels(ArrayList