diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ae3a77c2..54415858e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,10 @@ jobs: BRANCH: ${{ github.head_ref }} run: git checkout -B "$BRANCH" - - name: set up JDK 17 + - name: set up JDK uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: "temurin" cache: 'gradle' @@ -88,10 +88,10 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: set up JDK 17 + - name: set up JDK uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: "temurin" cache: 'gradle' @@ -121,10 +121,10 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 + - name: Set up JDK uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: "temurin" cache: 'gradle' diff --git a/app/build.gradle b/app/build.gradle index 00e3ad659..264a9db6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,7 @@ plugins { id "checkstyle" id "org.sonarqube" version "4.0.0.2929" id "org.jetbrains.kotlin.plugin.compose" version "${kotlin_version}" + id 'com.google.dagger.hilt.android' } android { @@ -94,6 +95,7 @@ android { buildFeatures { viewBinding true compose true + buildConfig true } packagingOptions { @@ -114,7 +116,7 @@ ext { androidxRoomVersion = '2.6.1' androidxWorkVersion = '2.8.1' - icepickVersion = '3.2.0' + stateSaverVersion = '1.4.1' exoPlayerVersion = '2.18.7' googleAutoServiceVersion = '1.1.1' groupieVersion = '2.10.1' @@ -190,6 +192,10 @@ sonar { } } +kapt { + correctErrorTypes true +} + dependencies { /** Desugaring **/ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.4' @@ -200,7 +206,8 @@ dependencies { // name and the commit hash with the commit hash of the (pushed) commit you want to test // This works thanks to JitPack: https://jitpack.io/ implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' - implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.2' + // WORKAROUND: v0.24.2 can't be resolved by jitpack -> use git commit hash instead + implementation 'com.github.TeamNewPipe:NewPipeExtractor:176da72cb4c3ec4679211339b0e59f6b01bf2f52' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ @@ -236,8 +243,9 @@ dependencies { /** Third-party libraries **/ // Instance state boilerplate elimination - implementation "frankiesardo:icepick:${icepickVersion}" - kapt "frankiesardo:icepick-processor:${icepickVersion}" + implementation 'com.github.livefront:bridge:v2.0.2' + implementation "com.evernote:android-state:$stateSaverVersion" + kapt "com.evernote:android-state-processor:$stateSaverVersion" // HTML parser implementation "org.jsoup:jsoup:1.17.2" @@ -286,18 +294,28 @@ dependencies { implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final" // Jetpack Compose - implementation(platform('androidx.compose:compose-bom:2024.09.00')) + implementation(platform('androidx.compose:compose-bom:2024.09.03')) implementation 'androidx.compose.material3:material3' implementation 'androidx.compose.material3.adaptive:adaptive' implementation 'androidx.activity:activity-compose' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.lifecycle:lifecycle-viewmodel-compose' + implementation 'androidx.compose.ui:ui-text' // Needed for parsing HTML to AnnotatedString + + // Jetpack Compose related dependencies implementation 'androidx.paging:paging-compose:3.3.2' - implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0' + implementation "androidx.navigation:navigation-compose" // Coroutines interop implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.8.1' + // Hilt + implementation("com.google.dagger:hilt-android:2.51.1") + kapt("com.google.dagger:hilt-compiler:2.51.1") + + // Scroll + implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0' + /** Debugging **/ // Memory leak detection debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 435c4e29b..215df0da5 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -13,15 +13,6 @@ ## Rules for ExoPlayer -keep class com.google.android.exoplayer2.** { *; } -## Rules for Icepick. Copy pasted from https://github.com/frankiesardo/icepick --dontwarn icepick.** --keep class icepick.** { *; } --keep class **$$Icepick { *; } --keepclasseswithmembernames class * { - @icepick.* ; -} --keepnames class * { @icepick.State *;} - ## Rules for OkHttp. Copy pasted from https://github.com/square/okhttp -dontwarn okhttp3.** -dontwarn okio.** diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d11de9f47..c44f8bf2c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -77,6 +77,11 @@ android:exported="false" android:label="@string/settings" /> + + . */ +@HiltAndroidApp public class App extends Application implements ImageLoaderFactory { public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private static final String TAG = App.class.toString(); @@ -105,6 +108,7 @@ public class App extends Application implements ImageLoaderFactory { Localization.getPreferredContentCountry(this)); Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext())); + BridgeStateSaverInitializer.init(this); StateSaver.init(this); initNotificationChannels(); diff --git a/app/src/main/java/org/schabi/newpipe/AppModule.kt b/app/src/main/java/org/schabi/newpipe/AppModule.kt new file mode 100644 index 000000000..0aaf2f72b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/AppModule.kt @@ -0,0 +1,22 @@ +package org.schabi.newpipe + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class AppModule { + + @Provides + @Singleton + fun providesSharedPreference(@ApplicationContext context: Context): SharedPreferences { + return PreferenceManager.getDefaultSharedPreferences(context) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/BaseFragment.java index 7a06771dd..a55a341e6 100644 --- a/app/src/main/java/org/schabi/newpipe/BaseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/BaseFragment.java @@ -10,8 +10,9 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import icepick.Icepick; -import icepick.State; +import com.evernote.android.state.State; +import com.livefront.bridge.Bridge; + public abstract class BaseFragment extends Fragment { protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); @@ -48,7 +49,7 @@ public abstract class BaseFragment extends Fragment { + "savedInstanceState = [" + savedInstanceState + "]"); } super.onCreate(savedInstanceState); - Icepick.restoreInstanceState(this, savedInstanceState); + Bridge.restoreInstanceState(this, savedInstanceState); if (savedInstanceState != null) { onRestoreInstanceState(savedInstanceState); } @@ -70,7 +71,7 @@ public abstract class BaseFragment extends Fragment { @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); - Icepick.saveInstanceState(this, outState); + Bridge.saveInstanceState(this, outState); } protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) { diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index c59dc7532..197c965ba 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -41,6 +41,9 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceManager; +import com.evernote.android.state.State; +import com.livefront.bridge.Bridge; + import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.databinding.ListRadioIconItemBinding; import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; @@ -98,8 +101,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import icepick.Icepick; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -152,7 +153,7 @@ public class RouterActivity extends AppCompatActivity { getWindow().setAttributes(params); super.onCreate(savedInstanceState); - Icepick.restoreInstanceState(this, savedInstanceState); + Bridge.restoreInstanceState(this, savedInstanceState); // FragmentManager will take care to recreate (Playlist|Download)Dialog when screen rotates // We used to .setOnDismissListener(dialog -> finish()); when creating these DialogFragments @@ -197,7 +198,7 @@ public class RouterActivity extends AppCompatActivity { @Override protected void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); - Icepick.saveInstanceState(this, outState); + Bridge.saveInstanceState(this, outState); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt index 0d0d0d48d..60a1cff37 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt @@ -138,8 +138,12 @@ class AboutActivity : AppCompatActivity() { "https://github.com/lisawray/groupie", StandardLicenses.MIT ), SoftwareComponent( - "Icepick", "2015", "Frankie Sardo", - "https://github.com/frankiesardo/icepick", StandardLicenses.EPL1 + "Android-State", "2018", "Evernote", + "https://github.com/Evernote/android-state", StandardLicenses.EPL1 + ), + SoftwareComponent( + "Bridge", "2021", "Livefront", + "https://github.com/livefront/bridge", StandardLicenses.APACHE2 ), SoftwareComponent( "Jsoup", "2009 - 2020", "Jonathan Hedley", diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index db2066b27..34a4ba022 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -39,6 +39,8 @@ import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.DialogFragment; import androidx.preference.PreferenceManager; +import com.evernote.android.state.State; +import com.livefront.bridge.Bridge; import com.nononsenseapps.filepicker.Utils; import org.schabi.newpipe.MainActivity; @@ -59,6 +61,8 @@ import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredDirectoryHelper; import org.schabi.newpipe.streams.io.StoredFileHelper; +import org.schabi.newpipe.util.AudioTrackAdapter; +import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper; import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.FilenameUtils; import org.schabi.newpipe.util.ListHelper; @@ -67,8 +71,6 @@ import org.schabi.newpipe.util.SecondaryStreamHelper; import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener; import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter.StreamInfoWrapper; -import org.schabi.newpipe.util.AudioTrackAdapter; -import org.schabi.newpipe.util.AudioTrackAdapter.AudioTracksWrapper; import org.schabi.newpipe.util.ThemeHelper; import java.io.File; @@ -79,8 +81,6 @@ import java.util.Locale; import java.util.Objects; import java.util.Optional; -import icepick.Icepick; -import icepick.State; import io.reactivex.rxjava3.disposables.CompositeDisposable; import us.shandian.giga.get.MissionRecoveryInfo; import us.shandian.giga.postprocessing.Postprocessing; @@ -214,7 +214,7 @@ public class DownloadDialog extends DialogFragment context = getContext(); setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); - Icepick.restoreInstanceState(this, savedInstanceState); + Bridge.restoreInstanceState(this, savedInstanceState); this.audioTrackAdapter = new AudioTrackAdapter(wrappedAudioTracks); this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams); @@ -372,7 +372,7 @@ public class DownloadDialog extends DialogFragment @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); - Icepick.saveInstanceState(this, outState); + Bridge.saveInstanceState(this, outState); } diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java index 831a8cc4b..2f607b487 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.error; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -13,7 +12,6 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -22,7 +20,6 @@ import androidx.core.content.IntentCompat; import com.grack.nanojson.JsonWriter; import org.schabi.newpipe.BuildConfig; -import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ActivityErrorBinding; import org.schabi.newpipe.util.Localization; @@ -187,25 +184,6 @@ public class ErrorActivity extends AppCompatActivity { .collect(Collectors.joining(separator + "\n", separator + "\n", separator)); } - /** - * Get the checked activity. - * - * @param returnActivity the activity to return to - * @return the casted return activity or null - */ - @Nullable - static Class getReturnActivity(final Class returnActivity) { - Class checkedReturnActivity = null; - if (returnActivity != null) { - if (Activity.class.isAssignableFrom(returnActivity)) { - checkedReturnActivity = returnActivity.asSubclass(Activity.class); - } else { - checkedReturnActivity = MainActivity.class; - } - } - return checkedReturnActivity; - } - private void buildInfo(final ErrorInfo info) { String text = ""; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index a3d3d8b60..8361953b9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -13,6 +13,8 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; +import com.evernote.android.state.State; + import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -22,8 +24,6 @@ import org.schabi.newpipe.util.InfoCache; import java.util.concurrent.atomic.AtomicBoolean; -import icepick.State; - public abstract class BaseStateFragment extends BaseFragment implements ViewContract { @State protected AtomicBoolean wasLoading = new AtomicBoolean(); @@ -134,6 +134,7 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC hideErrorPanel(); } + @Override public void showEmptyState() { isLoading.set(false); if (emptyStateView != null) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java index 581e54156..52fb3f29e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java @@ -11,6 +11,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.Description; @@ -19,8 +21,6 @@ import org.schabi.newpipe.util.Localization; import java.util.List; -import icepick.State; - public class DescriptionFragment extends BaseDescriptionFragment { @State @@ -31,7 +31,7 @@ public class DescriptionFragment extends BaseDescriptionFragment { } public DescriptionFragment() { - // keep empty constructor for IcePick when resuming fragment from memory + // keep empty constructor for State when resuming fragment from memory } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 20ea3c710..4f5bd9e94 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -56,6 +56,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; +import com.evernote.android.state.State; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.material.appbar.AppBarLayout; @@ -127,7 +128,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import coil.util.CoilUtils; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index dd5eb6c8a..61a361f23 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -9,6 +9,8 @@ import android.view.View; import androidx.annotation.NonNull; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -24,7 +26,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Queue; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.Disposable; @@ -143,7 +144,7 @@ public abstract class BaseListInfoFragment { + .subscribe((@NonNull final L result) -> { isLoading.set(false); currentInfo = result; currentNextPage = result.getNextPage(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index 0dc2fb65a..b7f4a9d3d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -10,6 +10,8 @@ import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.channel.ChannelInfo; @@ -20,8 +22,6 @@ import org.schabi.newpipe.util.Localization; import java.util.List; -import icepick.State; - public class ChannelAboutFragment extends BaseDescriptionFragment { @State protected ChannelInfo channelInfo; @@ -31,7 +31,7 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { } public ChannelAboutFragment() { - // keep empty constructor for IcePick when resuming fragment from memory + // keep empty constructor for State when resuming fragment from memory } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 3890e4865..56d8a9315 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -25,6 +25,7 @@ import androidx.core.graphics.ColorUtils; import androidx.core.view.MenuProvider; import androidx.preference.PreferenceManager; +import com.evernote.android.state.State; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayout; import com.jakewharton.rxbinding4.view.RxView; @@ -60,7 +61,6 @@ import java.util.Queue; import java.util.concurrent.TimeUnit; import coil.util.CoilUtils; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.CompositeDisposable; @@ -249,7 +249,7 @@ public class ChannelFragment extends BaseStateFragment //////////////////////////////////////////////////////////////////////////*/ private void monitorSubscription(final ChannelInfo info) { - final Consumer onError = (Throwable throwable) -> { + final Consumer onError = (final Throwable throwable) -> { animate(binding.channelSubscribeButton, false, 100); showSnackBarError(new ErrorInfo(throwable, UserAction.SUBSCRIPTION_GET, "Get subscription status", currentInfo)); @@ -284,14 +284,14 @@ public class ChannelFragment extends BaseStateFragment } private Function mapOnSubscribe(final SubscriptionEntity subscription) { - return (@NonNull Object o) -> { + return (@NonNull final Object o) -> { subscriptionManager.insertSubscription(subscription); return o; }; } private Function mapOnUnsubscribe(final SubscriptionEntity subscription) { - return (@NonNull Object o) -> { + return (@NonNull final Object o) -> { subscriptionManager.deleteSubscription(subscription); return o; }; @@ -318,7 +318,7 @@ public class ChannelFragment extends BaseStateFragment } private Disposable monitorSubscribeButton(final Function action) { - final Consumer onNext = (@NonNull Object o) -> { + final Consumer onNext = (@NonNull final Object o) -> { if (DEBUG) { Log.d(TAG, "Changed subscription status to this channel!"); } @@ -338,7 +338,7 @@ public class ChannelFragment extends BaseStateFragment } private Consumer> getSubscribeUpdateMonitor(final ChannelInfo info) { - return (List subscriptionEntities) -> { + return (final List subscriptionEntities) -> { if (DEBUG) { Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: " + "subscriptionEntities = [" + subscriptionEntities + "]"); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java index 95ac42eed..5d398821a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java @@ -9,6 +9,8 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PlaylistControlBinding; import org.schabi.newpipe.error.UserAction; @@ -32,13 +34,12 @@ import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; -import icepick.State; import io.reactivex.rxjava3.core.Single; public class ChannelTabFragment extends BaseListInfoFragment implements PlaylistControlViewHolder { - // states must be protected and not private for IcePick being able to access them + // states must be protected and not private for State being able to access them @State protected ListLinkHandler tabHandler; @State @@ -156,6 +157,7 @@ public class ChannelTabFragment extends BaseListInfoFragment streamItems = infoListAdapter.getItemsList().stream() .filter(StreamInfoItem.class::isInstance) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index b90dccb17..6823e13d3 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -11,6 +11,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -29,7 +31,6 @@ import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; -import icepick.State; import io.reactivex.rxjava3.core.Single; /** diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index eef3455ae..18c60400b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -40,6 +40,8 @@ import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; +import com.evernote.android.state.State; + import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.FragmentSearchBinding; import org.schabi.newpipe.error.ErrorInfo; @@ -77,7 +79,6 @@ import java.util.Queue; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -550,7 +551,7 @@ public class SearchFragment extends BaseListFragment { + searchEditText.setOnFocusChangeListener((final View v, final boolean hasFocus) -> { if (DEBUG) { Log.d(TAG, "onFocusChange() called with: " + "v = [" + v + "], hasFocus = [" + hasFocus + "]"); @@ -611,7 +612,7 @@ public class SearchFragment extends BaseListFragment { + (final TextView v, final int actionId, final KeyEvent event) -> { if (DEBUG) { Log.d(TAG, "onEditorAction() called with: v = [" + v + "], " + "actionId = [" + actionId + "], event = [" + event + "]"); diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index a366723e0..a5e1594d1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -19,6 +19,8 @@ import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; +import com.evernote.android.state.State; + import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.NewPipeDatabase; @@ -36,16 +38,15 @@ import org.schabi.newpipe.local.holder.LocalBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; -import org.schabi.newpipe.util.debounce.DebounceSavable; -import org.schabi.newpipe.util.debounce.DebounceSaver; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; +import org.schabi.newpipe.util.debounce.DebounceSavable; +import org.schabi.newpipe.util.debounce.DebounceSaver; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index b99291309..a4e53aab1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -44,11 +44,11 @@ import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.evernote.android.state.State import com.xwray.groupie.GroupieAdapter import com.xwray.groupie.Item import com.xwray.groupie.OnItemClickListener import com.xwray.groupie.OnItemLongClickListener -import icepick.State import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 1fea7e155..fac358075 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.viewbinding.ViewBinding; +import com.evernote.android.state.State; import com.google.android.material.snackbar.Snackbar; import org.reactivestreams.Subscriber; @@ -45,7 +46,6 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; @@ -368,6 +368,7 @@ public class StatisticsPlaylistFragment } } + @Override public PlayQueue getPlayQueue() { return getPlayQueue(0); } diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index d5ae431fa..c87d9cccc 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -26,6 +26,8 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; +import com.evernote.android.state.State; + import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.NewPipeDatabase; @@ -49,12 +51,12 @@ import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; -import org.schabi.newpipe.util.debounce.DebounceSavable; -import org.schabi.newpipe.util.debounce.DebounceSaver; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.PlayButtonHelper; +import org.schabi.newpipe.util.debounce.DebounceSavable; +import org.schabi.newpipe.util.debounce.DebounceSaver; import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; @@ -63,7 +65,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import icepick.State; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; @@ -843,6 +844,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment) { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index dfb49a25b..7e74c3848 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -24,6 +24,9 @@ import androidx.core.math.MathUtils; import androidx.fragment.app.DialogFragment; import androidx.preference.PreferenceManager; +import com.evernote.android.state.State; +import com.livefront.bridge.Bridge; + import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.DialogPlaybackParameterBinding; import org.schabi.newpipe.player.ui.VideoPlayerUi; @@ -37,9 +40,6 @@ import java.util.function.DoubleConsumer; import java.util.function.DoubleFunction; import java.util.function.DoubleSupplier; -import icepick.Icepick; -import icepick.State; - public class PlaybackParameterDialog extends DialogFragment { private static final String TAG = "PlaybackParameterDialog"; @@ -135,7 +135,7 @@ public class PlaybackParameterDialog extends DialogFragment { @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); - Icepick.saveInstanceState(this, outState); + Bridge.saveInstanceState(this, outState); } /*////////////////////////////////////////////////////////////////////////// @@ -146,7 +146,7 @@ public class PlaybackParameterDialog extends DialogFragment { @Override public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { assureCorrectAppLanguage(getContext()); - Icepick.restoreInstanceState(this, savedInstanceState); + Bridge.restoreInstanceState(this, savedInstanceState); binding = DialogPlaybackParameterBinding.inflate(getLayoutInflater()); initUI(); diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt b/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt new file mode 100644 index 000000000..ac08dd36b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt @@ -0,0 +1,27 @@ +package org.schabi.newpipe.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import org.schabi.newpipe.R +import org.schabi.newpipe.settings.viewmodel.SettingsViewModel +import org.schabi.newpipe.ui.SwitchPreference +import org.schabi.newpipe.ui.theme.SizeTokens + +@Composable +fun DebugScreen(viewModel: SettingsViewModel, modifier: Modifier = Modifier) { + + val settingsLayoutRedesign by viewModel.settingsLayoutRedesign.collectAsState() + + Column(modifier = modifier) { + SwitchPreference( + modifier = Modifier.padding(SizeTokens.SpacingExtraSmall), + R.string.settings_layout_redesign, + settingsLayoutRedesign, + viewModel::toggleSettingsLayoutRedesign + ) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java index 529e53442..0d57ce174 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -21,7 +21,9 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import com.evernote.android.state.State; import com.jakewharton.rxbinding4.widget.RxTextView; +import com.livefront.bridge.Bridge; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -41,9 +43,6 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.util.concurrent.TimeUnit; -import icepick.Icepick; -import icepick.State; - /* * Created by Christian Schabesberger on 31.08.15. * @@ -93,7 +92,7 @@ public class SettingsActivity extends AppCompatActivity implements assureCorrectAppLanguage(this); super.onCreate(savedInstanceBundle); - Icepick.restoreInstanceState(this, savedInstanceBundle); + Bridge.restoreInstanceState(this, savedInstanceBundle); final boolean restored = savedInstanceBundle != null; final SettingsLayoutBinding settingsLayoutBinding = @@ -125,7 +124,7 @@ public class SettingsActivity extends AppCompatActivity implements @Override protected void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); - Icepick.saveInstanceState(this, outState); + Bridge.saveInstanceState(this, outState); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt b/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt new file mode 100644 index 000000000..5bd8f2b08 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt @@ -0,0 +1,23 @@ +package org.schabi.newpipe.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import org.schabi.newpipe.R +import org.schabi.newpipe.ui.TextPreference + +@Composable +fun SettingsScreen( + onSelectSettingOption: (SettingsScreenKey) -> Unit, + modifier: Modifier = Modifier +) { + Column(modifier = modifier) { + TextPreference( + title = R.string.settings_category_debug_title, + onClick = { onSelectSettingOption(SettingsScreenKey.DEBUG) } + ) + HorizontalDivider(color = Color.Black) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt b/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt new file mode 100644 index 000000000..821ff0187 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt @@ -0,0 +1,85 @@ +package org.schabi.newpipe.settings + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import dagger.hilt.android.AndroidEntryPoint +import org.schabi.newpipe.R +import org.schabi.newpipe.settings.viewmodel.SettingsViewModel +import org.schabi.newpipe.ui.Toolbar +import org.schabi.newpipe.ui.theme.AppTheme + +const val SCREEN_TITLE_KEY = "SCREEN_TITLE_KEY" + +@AndroidEntryPoint +class SettingsV2Activity : ComponentActivity() { + + private val settingsViewModel: SettingsViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + val navController = rememberNavController() + var screenTitle by remember { mutableIntStateOf(SettingsScreenKey.ROOT.screenTitle) } + navController.addOnDestinationChangedListener { _, _, arguments -> + screenTitle = + arguments?.getInt(SCREEN_TITLE_KEY) ?: SettingsScreenKey.ROOT.screenTitle + } + + AppTheme { + Scaffold(topBar = { + Toolbar( + title = stringResource(id = screenTitle), + hasSearch = true, + onSearchQueryChange = null // TODO: Add suggestions logic + ) + }) { padding -> + NavHost( + navController = navController, + startDestination = SettingsScreenKey.ROOT.name, + modifier = Modifier.padding(padding) + ) { + composable( + SettingsScreenKey.ROOT.name, + listOf(createScreenTitleArg(SettingsScreenKey.ROOT.screenTitle)) + ) { + SettingsScreen(onSelectSettingOption = { screen -> + navController.navigate(screen.name) + }) + } + composable( + SettingsScreenKey.DEBUG.name, + listOf(createScreenTitleArg(SettingsScreenKey.DEBUG.screenTitle)) + ) { + DebugScreen(settingsViewModel) + } + } + } + } + } + } +} + +fun createScreenTitleArg(@StringRes screenTitle: Int) = navArgument(SCREEN_TITLE_KEY) { + defaultValue = screenTitle +} + +enum class SettingsScreenKey(@StringRes val screenTitle: Int) { + ROOT(R.string.settings), + DEBUG(R.string.settings_category_debug_title) +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt new file mode 100644 index 000000000..ae3520c94 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/viewmodel/SettingsViewModel.kt @@ -0,0 +1,39 @@ +package org.schabi.newpipe.settings.viewmodel + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.ContextCompat +import androidx.lifecycle.AndroidViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.schabi.newpipe.R +import javax.inject.Inject + +@HiltViewModel +class SettingsViewModel @Inject constructor( + @ApplicationContext context: Context, + private val preferenceManager: SharedPreferences +) : AndroidViewModel(context.applicationContext as Application) { + + private var _settingsLayoutRedesignPref: Boolean + get() = preferenceManager.getBoolean( + ContextCompat.getString(getApplication(), R.string.settings_layout_redesign_key), false + ) + set(value) { + preferenceManager.edit().putBoolean( + ContextCompat.getString(getApplication(), R.string.settings_layout_redesign_key), + value + ).apply() + } + private val _settingsLayoutRedesign: MutableStateFlow = + MutableStateFlow(_settingsLayoutRedesignPref) + val settingsLayoutRedesign = _settingsLayoutRedesign.asStateFlow() + + fun toggleSettingsLayoutRedesign(newState: Boolean) { + _settingsLayoutRedesign.value = newState + _settingsLayoutRedesignPref = newState + } +} diff --git a/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt b/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt new file mode 100644 index 000000000..d479343f5 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ui/SwitchPreference.kt @@ -0,0 +1,53 @@ +package org.schabi.newpipe.ui + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import org.schabi.newpipe.ui.theme.SizeTokens + +@Composable +fun SwitchPreference( + modifier: Modifier = Modifier, + @StringRes title: Int, + isChecked: Boolean, + onCheckedChange: (Boolean) -> Unit, + @StringRes summary: Int? = null +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = modifier.fillMaxWidth() + ) { + Column { + Text( + text = stringResource(id = title), + modifier = Modifier.padding(SizeTokens.SpacingExtraSmall), + style = MaterialTheme.typography.titleSmall, + textAlign = TextAlign.Start, + ) + summary?.let { + Text( + text = stringResource(id = summary), + modifier = Modifier.padding(SizeTokens.SpacingExtraSmall), + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Start, + ) + } + } + Spacer(modifier = Modifier.width(SizeTokens.SpacingSmall)) + Switch(checked = isChecked, onCheckedChange = onCheckedChange) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt b/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt new file mode 100644 index 000000000..f58f2f305 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/ui/TextPreference.kt @@ -0,0 +1,66 @@ +package org.schabi.newpipe.ui + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import org.schabi.newpipe.ui.theme.SizeTokens + +@Composable +fun TextPreference( + modifier: Modifier = Modifier, + @StringRes title: Int, + @DrawableRes icon: Int? = null, + @StringRes summary: Int? = null, + onClick: () -> Unit, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = modifier + .fillMaxWidth() + .padding(SizeTokens.SpacingSmall) + .defaultMinSize(minHeight = SizeTokens.SpaceMinSize) + .clickable { onClick() } + ) { + icon?.let { + Icon( + painter = painterResource(id = icon), + contentDescription = "icon for $title preference" + ) + Spacer(modifier = Modifier.width(SizeTokens.SpacingSmall)) + } + Column { + Text( + text = stringResource(id = title), + modifier = Modifier.padding(SizeTokens.SpacingExtraSmall), + style = MaterialTheme.typography.titleSmall, + textAlign = TextAlign.Start, + ) + summary?.let { + Text( + text = stringResource(id = summary), + modifier = Modifier.padding(SizeTokens.SpacingExtraSmall), + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Start, + ) + } + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java b/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java new file mode 100644 index 000000000..aeda4717c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/BridgeStateSaverInitializer.java @@ -0,0 +1,61 @@ +package org.schabi.newpipe.util; + +import android.content.Context; +import android.os.Bundle; +import android.os.Parcelable; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.evernote.android.state.StateSaver; +import com.livefront.bridge.Bridge; +import com.livefront.bridge.SavedStateHandler; +import com.livefront.bridge.ViewSavedStateHandler; + +/** + * Configures Bridge's state saver. + */ +public final class BridgeStateSaverInitializer { + + public static void init(final Context context) { + Bridge.initialize( + context, + new SavedStateHandler() { + @Override + public void saveInstanceState( + @NonNull final Object target, + @NonNull final Bundle state) { + StateSaver.saveInstanceState(target, state); + } + + @Override + public void restoreInstanceState( + @NonNull final Object target, + @Nullable final Bundle state) { + StateSaver.restoreInstanceState(target, state); + } + }, + new ViewSavedStateHandler() { + @NonNull + @Override + public Parcelable saveInstanceState( + @NonNull final T target, + @Nullable final Parcelable parentState) { + return StateSaver.saveInstanceState(target, parentState); + } + + @Nullable + @Override + public Parcelable restoreInstanceState( + @NonNull final T target, + @Nullable final Parcelable state) { + return StateSaver.restoreInstanceState(target, state); + } + } + ); + } + + private BridgeStateSaverInitializer() { + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 139381867..e05142c7a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -21,6 +21,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import androidx.preference.PreferenceManager; import com.jakewharton.processphoenix.ProcessPhoenix; @@ -64,6 +65,7 @@ import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.settings.SettingsActivity; +import org.schabi.newpipe.settings.SettingsV2Activity; import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.List; @@ -640,7 +642,13 @@ public final class NavigationHelper { } public static void openSettings(final Context context) { - final Intent intent = new Intent(context, SettingsActivity.class); + final Class settingsClass = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean( + ContextCompat.getString(context, R.string.settings_layout_redesign_key), + false + ) ? SettingsV2Activity.class : SettingsActivity.class; + + final Intent intent = new Intent(context, settingsClass); context.startActivity(intent); } diff --git a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java index f79e1e3a3..91b5ebd07 100644 --- a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java +++ b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java @@ -19,6 +19,9 @@ package org.schabi.newpipe.views; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static java.lang.annotation.RetentionPolicy.SOURCE; + import android.animation.ValueAnimator; import android.content.Context; import android.os.Parcelable; @@ -29,18 +32,15 @@ import android.widget.LinearLayout; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import com.evernote.android.state.State; +import com.livefront.bridge.Bridge; + import org.schabi.newpipe.ktx.ViewUtils; import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.List; -import icepick.Icepick; -import icepick.State; - -import static java.lang.annotation.RetentionPolicy.SOURCE; -import static org.schabi.newpipe.MainActivity.DEBUG; - /** * A view that can be fully collapsed and expanded. */ @@ -207,12 +207,12 @@ public class CollapsibleView extends LinearLayout { @Nullable @Override public Parcelable onSaveInstanceState() { - return Icepick.saveInstanceState(this, super.onSaveInstanceState()); + return Bridge.saveInstanceState(this, super.onSaveInstanceState()); } @Override public void onRestoreInstanceState(final Parcelable state) { - super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state)); + super.onRestoreInstanceState(Bridge.restoreInstanceState(this, state)); ready(); } diff --git a/app/src/main/res/layout-land/list_stream_card_item.xml b/app/src/main/res/layout-land/list_stream_card_item.xml deleted file mode 120000 index 70228ee1d..000000000 --- a/app/src/main/res/layout-land/list_stream_card_item.xml +++ /dev/null @@ -1 +0,0 @@ -../layout/list_stream_item.xml \ No newline at end of file diff --git a/app/src/main/res/layout-land/list_stream_card_item.xml b/app/src/main/res/layout-land/list_stream_card_item.xml new file mode 100644 index 000000000..793942568 --- /dev/null +++ b/app/src/main/res/layout-land/list_stream_card_item.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index e31cebb92..b0fceb89b 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -246,6 +246,7 @@ crash_the_app_key show_error_snackbar_key create_error_notification_key + settings_layout_redesign_key theme diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0539a0daf..cadf13309 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -492,6 +492,7 @@ Crash the app Show an error snackbar Create an error notification + Enable the Redesigned Settings page Import Import from diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index d97c5aa1a..5b6909892 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -64,4 +64,11 @@ android:title="@string/create_error_notification" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + diff --git a/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java b/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java deleted file mode 100644 index f77c7b268..000000000 --- a/app/src/test/java/org/schabi/newpipe/error/ErrorActivityTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.schabi.newpipe.error; - -import android.app.Activity; - -import org.junit.Test; -import org.schabi.newpipe.MainActivity; -import org.schabi.newpipe.RouterActivity; -import org.schabi.newpipe.fragments.detail.VideoDetailFragment; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -/** - * Unit tests for {@link ErrorActivity}. - */ -public class ErrorActivityTest { - @Test - public void getReturnActivity() { - Class returnActivity; - returnActivity = ErrorActivity.getReturnActivity(MainActivity.class); - assertEquals(MainActivity.class, returnActivity); - - returnActivity = ErrorActivity.getReturnActivity(RouterActivity.class); - assertEquals(RouterActivity.class, returnActivity); - - returnActivity = ErrorActivity.getReturnActivity(null); - assertNull(returnActivity); - - returnActivity = ErrorActivity.getReturnActivity(Integer.class); - assertEquals(MainActivity.class, returnActivity); - - returnActivity = ErrorActivity.getReturnActivity(VideoDetailFragment.class); - assertEquals(MainActivity.class, returnActivity); - } -} diff --git a/build.gradle b/build.gradle index 1acfb6f4a..25568c220 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '2.0.0' + ext.kotlin_version = '2.0.21' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.0' + classpath 'com.android.tools.build:gradle:8.7.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/doc/README.asm.md b/doc/README.asm.md index 2a7c10f51..370319c15 100644 --- a/doc/README.asm.md +++ b/doc/README.asm.md @@ -106,7 +106,7 @@ NewPipe এ আপুনি ব্যৱহাৰ কৰা সেৱাৰ অ ## অৱদান -আপোনাৰ ধাৰণা, অনুবাদ, ডিজাইন পৰিবৰ্তন, ক'ড পৰিষ্কাৰ কৰা, বা আনকি ডাঙৰ ক'ড পৰিৱৰ্তন হওক, সহায় সদায় আদৰণীয়। প্ৰতিটো অৱদানৰ লগে লগে এপটো ভাল হৈ পৰে, যিমানেই ডাঙৰ বা সৰু নহওক কিয়! যদি আপুনি জড়িত হ'ব বিচাৰে তেন্তে চাওক আমাৰ [অবদানৰ টোকা সমূহ](.github/CONTRIBUTING.md).Translation status +আপোনাৰ ধাৰণা, অনুবাদ, ডিজাইন পৰিবৰ্তন, ক'ড পৰিষ্কাৰ কৰা, বা আনকি ডাঙৰ ক'ড পৰিৱৰ্তন হওক, সহায় সদায় আদৰণীয়। প্ৰতিটো অৱদানৰ লগে লগে এপটো ভাল হৈ পৰে, যিমানেই ডাঙৰ বা সৰু নহওক কিয়! যদি আপুনি জড়িত হ'ব বিচাৰে তেন্তে চাওক আমাৰ [অবদানৰ টোকা সমূহ](/.github/CONTRIBUTING.md).Translation status ## অনুদান diff --git a/doc/README.de.md b/doc/README.de.md index e269da05c..be7744332 100644 --- a/doc/README.de.md +++ b/doc/README.de.md @@ -126,7 +126,7 @@ So eine Aktion wird nicht unterstützt und du solltest sie nur in Erwägung zieh ## Beitrag Egal ob du neue Ideen, Übersetzungen, Designvorschläge, kleine Code-Bereinigungen, oder sogar große Code-Verbesserungen hast, jegliche Unterstützung ist immer gern gesehen. Die App wird mit _jedem_ Beitrag besser und besser - egal wie viel Arbeit in ihn gesteckt wird! -Wenn du dich einbringen willst, sehe dir die [Beitragshinweise](.github/CONTRIBUTING.md) an. +Wenn du dich einbringen willst, sieh dir die [Beitragshinweise](/.github/CONTRIBUTING.md) an. Übersetzt diff --git a/doc/README.fr.md b/doc/README.fr.md index 7d4673b69..864cc927a 100644 --- a/doc/README.fr.md +++ b/doc/README.fr.md @@ -109,7 +109,7 @@ Entre temps, si vous voulez changer de source pour une raison quelconque (par ex ## Contribuer -Que vous ayez des idées, des traductions, des changements de design, du nettoyage de code, ou encore un changement de code majeur, toute aide est la bienvenue. L'app s'améliore un peu plus à chaque contribution, peu importe qu'elle soit grosse ou petite ! Si vous aimeriez être impliqué, jetez un coup d'oeil à nos [notes pour contribuer](.github/CONTRIBUTING.md). +Que vous ayez des idées, des traductions, des changements de design, du nettoyage de code, ou encore un changement de code majeur, toute aide est la bienvenue. L'app s'améliore un peu plus à chaque contribution, peu importe qu'elle soit grosse ou petite ! Si vous aimeriez être impliqué, jetez un coup d'oeil à nos [notes pour contribuer](/.github/CONTRIBUTING.md). Translation status diff --git a/doc/README.hi.md b/doc/README.hi.md index 282e75420..d503f43a5 100644 --- a/doc/README.hi.md +++ b/doc/README.hi.md @@ -105,7 +105,7 @@ NewPipe पर कई सेवाएँ उपलब्ध हैं। हम चाहे आप अपने विचार जोड़ना चाहे, या अनुवाद, डिज़ाइन में बदलाव, कोड में सफ़ाई, या कोड में भारी बदलाव, सहायता ज़रूर करें। जितने योगदान हो, ऐप उतनी ही बेहतर होती जाती है! -अगर आप योगदान करना चाहते हैं, हमारे [योगदान के दिशानिर्देश](.github/CONTRIBUTING.md) देखें। +अगर आप योगदान करना चाहते हैं, हमारे [योगदान के दिशानिर्देश](/.github/CONTRIBUTING.md) देखें। अनुवाद की स्थिति diff --git a/doc/README.it.md b/doc/README.it.md index 55ae12380..8bf1eb380 100644 --- a/doc/README.it.md +++ b/doc/README.it.md @@ -107,7 +107,7 @@ Nel frattempo, se vuoi cambiare fonte per la stessa ragione (ad es. la funzional ## Contribuire -Se hai idee, traduzioni, cambiamenti di *design*, pulizia di codice, o addirittura grossi cambiamenti di codice, l'aiuto è sempre apprezzato. L'app diventa sempre meglio con ogni contribuzione, non importa quanto grande o piccola essa sia! Se ti piacerebbe essere parte del progetto, vedi le nostre [note di contribuzione](.github/CONTRIBUTING.md). +Se hai idee, traduzioni, cambiamenti di *design*, pulizia di codice, o addirittura grossi cambiamenti di codice, l'aiuto è sempre apprezzato. L'app diventa sempre meglio con ogni contribuzione, non importa quanto grande o piccola essa sia! Se ti piacerebbe essere parte del progetto, vedi le nostre [note di contribuzione](/.github/CONTRIBUTING.md). Stato traduzione diff --git a/doc/README.pa.md b/doc/README.pa.md index 321e6b7d0..0ad5e0625 100644 --- a/doc/README.pa.md +++ b/doc/README.pa.md @@ -105,7 +105,7 @@ NewPipe ਤੁਹਾਡੇ ਦੁਆਰਾ ਵਰਤੀ ਜਾ ਰਹੀ ਸੇ ਨੋਟ: ਜਦੋਂ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਵਿੱਚ ਇੱਕ ਡੇਟਾਬੇਸ ਨੂੰ ਆਯਾਤ ਕਰ ਰਹੇ ਹੋ, ਤਾਂ ਹਮੇਸ਼ਾਂ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਇਹ ਉਹੀ ਹੈ ਜੋ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਤੋਂ ਨਿਰਯਾਤ ਕੀਤਾ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਅਧਿਕਾਰਤ ਐਪ ਤੋਂ ਇਲਾਵਾ ਕਿਸੇ ਏਪੀਕੇ ਤੋਂ ਨਿਰਯਾਤ ਕੀਤੇ ਡੇਟਾਬੇਸ ਨੂੰ ਆਯਾਤ ਕਰਦੇ ਹੋ, ਤਾਂ ਇਹ ਚੀਜ਼ਾਂ ਨੂੰ ਤੋੜ ਸਕਦਾ ਹੈ। ਅਜਿਹੀ ਕਾਰਵਾਈ ਅਸਮਰਥਿਤ ਹੈ, ਅਤੇ ਤੁਹਾਨੂੰ ਅਜਿਹਾ ਉਦੋਂ ਹੀ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜਦੋਂ ਤੁਹਾਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਯਕੀਨ ਹੋਵੇ ਕਿ ਤੁਸੀਂ ਜਾਣਦੇ ਹੋ ਕਿ ਤੁਸੀਂ ਕੀ ਕਰ ਰਹੇ ਹੋ। ## ਯੋਗਦਾਨ -ਭਾਵੇਂ ਤੁਹਾਡੇ ਕੋਲ ਵਿਚਾਰ, ਅਨੁਵਾਦ, ਡਿਜ਼ਾਈਨ ਤਬਦੀਲੀਆਂ, ਕੋਡ ਦੀ ਸਫਾਈ, ਜਾਂ ਇੱਥੋਂ ਤੱਕ ਕਿ ਵੱਡੀਆਂ ਕੋਡ ਤਬਦੀਲੀਆਂ ਹੋਣ, ਮਦਦ ਦਾ ਹਮੇਸ਼ਾ ਸਵਾਗਤ ਹੈ। ਐਪ ਹਰੇਕ ਯੋਗਦਾਨ ਦੇ ਨਾਲ ਬਿਹਤਰ ਅਤੇ ਬਿਹਤਰ ਹੋ ਜਾਂਦੀ ਹੈ, ਚਾਹੇ ਉਹ ਕਿੰਨਾ ਵੱਡਾ ਜਾਂ ਛੋਟਾ ਹੋਵੇ! ਜੇਕਰ ਤੁਸੀਂ ਸ਼ਾਮਲ ਹੋਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਸਾਡੀ ਜਾਂਚ ਕਰੋ [contribution notes](.github/CONTRIBUTING.md). +ਭਾਵੇਂ ਤੁਹਾਡੇ ਕੋਲ ਵਿਚਾਰ, ਅਨੁਵਾਦ, ਡਿਜ਼ਾਈਨ ਤਬਦੀਲੀਆਂ, ਕੋਡ ਦੀ ਸਫਾਈ, ਜਾਂ ਇੱਥੋਂ ਤੱਕ ਕਿ ਵੱਡੀਆਂ ਕੋਡ ਤਬਦੀਲੀਆਂ ਹੋਣ, ਮਦਦ ਦਾ ਹਮੇਸ਼ਾ ਸਵਾਗਤ ਹੈ। ਐਪ ਹਰੇਕ ਯੋਗਦਾਨ ਦੇ ਨਾਲ ਬਿਹਤਰ ਅਤੇ ਬਿਹਤਰ ਹੋ ਜਾਂਦੀ ਹੈ, ਚਾਹੇ ਉਹ ਕਿੰਨਾ ਵੱਡਾ ਜਾਂ ਛੋਟਾ ਹੋਵੇ! ਜੇਕਰ ਤੁਸੀਂ ਸ਼ਾਮਲ ਹੋਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਸਾਡੀ ਜਾਂਚ ਕਰੋ [contribution notes](/.github/CONTRIBUTING.md). Translation status diff --git a/doc/README.pt_BR.md b/doc/README.pt_BR.md index 59ff65db3..73489bb41 100644 --- a/doc/README.pt_BR.md +++ b/doc/README.pt_BR.md @@ -106,7 +106,7 @@ Enquanto isso, se você quiser trocar de fontes por algum motivo (por exemplo, a 4. Importe os dados da etapa 1 via Configurações > Backup e Restauração > Importar Base de Dados ## Contribuições -Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda. O aplicativo fica cada vez melhor a cada contribuição, não importa quão grande ou pequena! Se você quiser se envolver, verifique nossas [notas de contribuição](.github/CONTRIBUTING.md). +Se você tem ideias, traduções, alterações de design, limpeza de códigos ou mudanças reais de código, a ajuda é sempre bem-vinda. O aplicativo fica cada vez melhor a cada contribuição, não importa quão grande ou pequena! Se você quiser se envolver, verifique nossas [notas de contribuição](/.github/CONTRIBUTING.md). Estado da tradução diff --git a/doc/README.ru.md b/doc/README.ru.md index 35058c981..35867b8bf 100644 --- a/doc/README.ru.md +++ b/doc/README.ru.md @@ -106,7 +106,7 @@ NewPipe работает, извлекая необходимые данные Примечание: когда вы импортируете базу данных в официальное приложение, убедитесь, что это именно та база данных, которую вы экспортировали _из_ официального приложения. Если вы импортируете базу данных, экспортированную из APK, отличного от официального приложения, это может привести к ошибке. Такое действие не поддерживается, и вы должны делать его только тогда, когда абсолютно уверены, что знаете, что делаете. ## Участие -Если у вас есть идеи, переводы, изменения дизайна, очистка кода или даже серьезные изменения кода, помощь всегда приветствуется. Приложение становится всё лучше и лучше с каждым вкладом, независимо от того, большой он или маленький! Если вы хотите принять участие, ознакомьтесь с нашими [заметками об участии](.github/CONTRIBUTING.md). +Если у вас есть идеи, переводы, изменения дизайна, очистка кода или даже серьезные изменения кода, помощь всегда приветствуется. Приложение становится всё лучше и лучше с каждым вкладом, независимо от того, большой он или маленький! Если вы хотите принять участие, ознакомьтесь с нашими [заметками об участии](/.github/CONTRIBUTING.md). Состояние перевода diff --git a/doc/README.sr.md b/doc/README.sr.md index 7f0ee65b7..60a21ce69 100644 --- a/doc/README.sr.md +++ b/doc/README.sr.md @@ -104,7 +104,7 @@ NewPipe ради тако што преузима потребне податк Напомена: када увозите базу података у званичну апликацију, увек се уверите да је то она коју сте извезли _из_ званичне апликације. Ако увезете базу података извезену из APK-а, који није званична апликација, то може покварити ствари. Таква радња није подржана и требало би да то урадите само када сте потпуно сигурни да знате шта радите. ## Допринос -Без обзира да ли имате идеје, преводе, промене дизајна, чишћење кода или чак велике промене кода, помоћ је увек добродошла. Апликација постаје све боља и боља са сваким доприносом, без обзира колико је он велики или мали! Ако желите да се укључите, погледајте наше [напомене о доприносима](.github/CONTRIBUTING.md). +Без обзира да ли имате идеје, преводе, промене дизајна, чишћење кода или чак велике промене кода, помоћ је увек добродошла. Апликација постаје све боља и боља са сваким доприносом, без обзира колико је он велики или мали! Ако желите да се укључите, погледајте наше [напомене о доприносима](/.github/CONTRIBUTING.md). Статус превода diff --git a/gradle.properties b/gradle.properties index 0ca913222..ed32303da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,3 @@ -android.defaults.buildfeatures.buildconfig=true android.enableJetifier=false android.nonFinalResIds=false android.nonTransitiveRClass=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d022615ff..4ea536e77 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=38f66cd6eef217b4c35855bb11ea4e9fbc53594ccccb5fb82dfd317ef8c2c5a3 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists