diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..6419c65dd52 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: [push, pull_request] + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: set up JDK 1.8 + uses: actions/setup-java@v1.4.3 + with: + java-version: 1.8 + + - name: Cache Gradle dependencies + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + + - name: Build debug APK and run Tests + run: ./gradlew assembleDebug lintDebug testDebugUnitTest --stacktrace + + - name: Upload APK + uses: actions/upload-artifact@v2 + with: + name: app + path: app/build/outputs/apk/debug/*.apk diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1714c70d563..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: android -jdk: - - oraclejdk8 -android: - components: - # The BuildTools version used by NewPipe - - tools - - build-tools-29.0.3 - - # The SDK version used to compile NewPipe - - android-29 - -before_install: - - yes | sdkmanager "platforms;android-29" -script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest - -licenses: - - '.+' diff --git a/README.ko.md b/README.ko.md index bb6bd653b0a..a86eae8d939 100644 --- a/README.ko.md +++ b/README.ko.md @@ -1,4 +1,4 @@ -

+

NewPipe

A libre lightweight streaming frontend for Android.

@@ -13,7 +13,7 @@


ScreenshotsDescriptionFeaturesUpdatesContributionDonateLicense

-

WebsiteBlogFAQPress

+

WebsiteBlogFAQPress


*Read this in other languages: [English](README.md), [한국어](README.ko.md).* @@ -86,7 +86,7 @@ NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 1. 직접 디버그 APK를 생성할 수 있습니다. 이 방법은 당신의 기기에서 새로운 기능을 얻을 수 있는 가장 빠른 방법이지만, 꽤 많이 복잡합니다. 따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다. 2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다. - 이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 이에 대한 설명서는 이곳에서 확인할 수 있습니다: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/ 3. 우리가 릴리즈를 게시하는 대로 [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases)에서 APK를 다운받고 이것을 설치할 수 있습니다. 4. F-Droid를 통해 업데이트 할 수 있습니다. F-Droid는 변화를 인식하고, 스스로 APK를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에, 이것은 업데이트를 받는 가장 느린 방법입니다. @@ -111,7 +111,7 @@ NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 ## Donate -만약 NewPipe가 마음에 들었다면, 우리는 기부에 대해 기꺼이 환영합니다. bitcoin을 보내거나, Bountysource 또는 Liberapay를 통해 기부할 수 있습니다. NewPipe에 기부하는 것에 대한 자세한 정보를 원한다면, 우리의 [웹사이트](https://newpipe.schabi.org/donate)를 방문하여 주십시오. +만약 NewPipe가 마음에 들었다면, 우리는 기부에 대해 기꺼이 환영합니다. bitcoin을 보내거나, Bountysource 또는 Liberapay를 통해 기부할 수 있습니다. NewPipe에 기부하는 것에 대한 자세한 정보를 원한다면, 우리의 [웹사이트](https://newpipe.net/donate)를 방문하여 주십시오. @@ -134,7 +134,7 @@ NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 ## Privacy Policy NewPipe 프로젝트는 미디어 웹 서비스를 사용하는 것에 대한 사적의, 익명의 경험을 제공하는 것을 목표로 하고 있습니다. -그러므로, 앱은 당신의 동의 없이 어떤 데이터도 수집하지 않습니다. NewPipe의 개인정보보호정책은 당신이 충돌 리포트를 보내거나, 또는 우리의 블로그에 글을 남길 때 어떤 데이터가 보내지고 저장되는지에 대해 상세히 설명합니다. 이 문서는 [여기](https://newpipe.schabi.org/legal/privacy/)에서 확인할 수 있습니다. +그러므로, 앱은 당신의 동의 없이 어떤 데이터도 수집하지 않습니다. NewPipe의 개인정보보호정책은 당신이 충돌 리포트를 보내거나, 또는 우리의 블로그에 글을 남길 때 어떤 데이터가 보내지고 저장되는지에 대해 상세히 설명합니다. 이 문서는 [여기](https://newpipe.net/legal/privacy/)에서 확인할 수 있습니다. ## License [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) diff --git a/README.md b/README.md index 2568b7624b8..c66bcfa7fb9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

NewPipe

A libre lightweight streaming frontend for Android.

@@ -13,7 +13,7 @@


ScreenshotsDescriptionFeaturesUpdatesContributionDonateLicense

-

WebsiteBlogFAQPress

+

WebsiteBlogFAQPress


*Read this in other languages: [English](README.md), [한국어](README.ko.md).* @@ -83,7 +83,7 @@ NewPipe supports multiple services. Our [docs](https://teamnewpipe.github.io/doc ## Updates When a change to the NewPipe code occurs (due to either adding features or bug fixing), eventually a release will occur. These are in the format x.xx.x . In order to get this new version, you can: 1. Build a debug APK yourself. This is the fastest way to get new features on your device, but is much more complicated, so we recommend using one of the other methods. - 2. Add our custom repo to F-Droid and install it from there as soon as we publish a release. The instructions are here: https://newpipe.schabi.org/FAQ/tutorials/install-add-fdroid-repo/ + 2. Add our custom repo to F-Droid and install it from there as soon as we publish a release. The instructions are here: https://newpipe.net/FAQ/tutorials/install-add-fdroid-repo/ 3. Download the APK from [Github Releases](https://github.com/TeamNewPipe/NewPipe/releases) and install it as soon as we publish a release. 4. Update via F-droid. This is the slowest method of getting updates, as F-Droid must recognize changes, build the APK itself, sign it, then push the update to users. @@ -106,7 +106,7 @@ If you'd like to get involved, check our [contribution notes](.github/CONTRIBUTI ## Donate -If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.schabi.org/donate). +If you like NewPipe we'd be happy about a donation. You can either send bitcoin or donate via Bountysource or Liberapay. For further info on donating to NewPipe, please visit our [website](https://newpipe.net/donate).
@@ -129,7 +129,7 @@ If you like NewPipe we'd be happy about a donation. You can either send bitcoin ## Privacy Policy The NewPipe project aims to provide a private, anonymous experience for using media web services. -Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or comment in our blog. You can find the document [here](https://newpipe.schabi.org/legal/privacy/). +Therefore, the app does not collect any data without your consent. NewPipe's privacy policy explains in detail what data is sent and stored when you send a crash report, or comment in our blog. You can find the document [here](https://newpipe.net/legal/privacy/). ## License [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) diff --git a/app/build.gradle b/app/build.gradle index 87215b38523..522c3606295 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { resValue "string", "app_name", "NewPipe" minSdkVersion 19 targetSdkVersion 29 - versionCode 960 - versionName "0.20.6" + versionCode 961 + versionName "0.20.7" multiDexEnabled true @@ -85,11 +85,15 @@ android { sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } + + buildFeatures { + viewBinding true + } } ext { icepickVersion = '3.2.0' - checkstyleVersion = '8.37' + checkstyleVersion = '8.38' stethoVersion = '1.5.1' leakCanaryVersion = '2.5' exoPlayerVersion = '2.11.8' @@ -162,7 +166,7 @@ dependencies { kapt "frankiesardo:icepick-processor:${icepickVersion}" checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}" - ktlint "com.pinterest:ktlint:0.39.0" + ktlint "com.pinterest:ktlint:0.40.0" debugImplementation "com.facebook.stetho:stetho:${stethoVersion}" debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}" @@ -175,7 +179,7 @@ dependencies { // NewPipe dependencies // You can use a local version by uncommenting a few lines in settings.gradle - implementation 'com.github.TeamNewPipe:NewPipeExtractor:b3835bd616ab28b861c83dcefd56e1754c6d20be' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:b2837698f55296e00aeca5cb1847755dd1174af4' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751" implementation "org.jsoup:jsoup:1.13.1" @@ -199,6 +203,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' + implementation 'androidx.webkit:webkit:1.4.0' implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}" implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b9de562016..d240d123f75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -227,20 +227,18 @@ + - - - - - + + + + + + + + - - - - - - @@ -268,7 +266,7 @@ - + diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index de401d4f5d9..e6dce4d6763 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -242,8 +242,9 @@ private void initNotificationChannels() { String name = getString(R.string.notification_channel_name); String description = getString(R.string.notification_channel_description); - // Keep this below DEFAULT to avoid making noise on every notification update - final int importance = NotificationManager.IMPORTANCE_LOW; + // Keep this below DEFAULT to avoid making noise on every notification update for the main + // and update channels + int importance = NotificationManager.IMPORTANCE_LOW; final NotificationChannel mainChannel = new NotificationChannel(id, name, importance); mainChannel.setDescription(description); @@ -255,9 +256,17 @@ private void initNotificationChannels() { final NotificationChannel appUpdateChannel = new NotificationChannel(id, name, importance); appUpdateChannel.setDescription(description); + id = getString(R.string.hash_channel_id); + name = getString(R.string.hash_channel_name); + description = getString(R.string.hash_channel_description); + importance = NotificationManager.IMPORTANCE_HIGH; + + final NotificationChannel hashChannel = new NotificationChannel(id, name, importance); + hashChannel.setDescription(description); + final NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannels(Arrays.asList(mainChannel, - appUpdateChannel)); + appUpdateChannel, hashChannel)); } protected boolean isDisposedRxExceptionsReported() { diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java index 916630ae6e4..0fecc3f96d3 100644 --- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java +++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java @@ -48,7 +48,7 @@ private CheckForNewAppVersion() { } private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15"; - private static final String NEWPIPE_API_URL = "https://newpipe.schabi.org/api/data.json"; + private static final String NEWPIPE_API_URL = "https://newpipe.net/api/data.json"; /** * Method to get the APK's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133. diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index f51ecf2d370..0c784e9d50a 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -39,17 +39,14 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.Button; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.Spinner; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; @@ -57,8 +54,12 @@ import androidx.preference.PreferenceManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.navigation.NavigationView; +import org.schabi.newpipe.databinding.ActivityMainBinding; +import org.schabi.newpipe.databinding.DrawerHeaderBinding; +import org.schabi.newpipe.databinding.DrawerLayoutBinding; +import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding; +import org.schabi.newpipe.databinding.ToolbarLayoutBinding; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -96,15 +97,14 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); + private ActivityMainBinding mainBinding; + private DrawerHeaderBinding drawerHeaderBinding; + private DrawerLayoutBinding drawerLayoutBinding; + private ToolbarLayoutBinding toolbarLayoutBinding; + private ActionBarDrawerToggle toggle; - private DrawerLayout drawer; - private NavigationView drawerItems; - private ImageView headerServiceIcon; - private TextView headerServiceView; - private Button toggleServiceButton; private boolean servicesShown = false; - private ImageView serviceArrow; private BroadcastReceiver broadcastReceiver; @@ -129,7 +129,7 @@ protected void onCreate(final Bundle savedInstanceState) { + "savedInstanceState = [" + savedInstanceState + "]"); } - // enable TLS1.1/1.2 for kitkat devices, to fix download and play for mediaCCC sources + // enable TLS1.1/1.2 for kitkat devices, to fix download and play for media.ccc.de sources if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { TLSSocketFactoryCompat.setAsDefault(); } @@ -137,13 +137,19 @@ protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + + mainBinding = ActivityMainBinding.inflate(getLayoutInflater()); + drawerLayoutBinding = mainBinding.drawerLayout; + drawerHeaderBinding = DrawerHeaderBinding.bind(drawerLayoutBinding.navigation + .getHeaderView(0)); + toolbarLayoutBinding = mainBinding.toolbarLayout; + setContentView(mainBinding.getRoot()); if (getSupportFragmentManager().getBackStackEntryCount() == 0) { initFragments(); } - setSupportActionBar(findViewById(R.id.toolbar)); + setSupportActionBar(toolbarLayoutBinding.toolbar); try { setupDrawer(); } catch (final Exception e) { @@ -157,10 +163,6 @@ protected void onCreate(final Bundle savedInstanceState) { } private void setupDrawer() throws Exception { - final Toolbar toolbar = findViewById(R.id.toolbar); - drawer = findViewById(R.id.drawer_layout); - drawerItems = findViewById(R.id.navigation); - //Tabs final int currentServiceId = ServiceHelper.getSelectedServiceId(this); final StreamingService service = NewPipe.getService(currentServiceId); @@ -168,43 +170,43 @@ private void setupDrawer() throws Exception { int kioskId = 0; for (final String ks : service.getKioskList().getAvailableKiosks()) { - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator .getTranslatedKioskName(ks, this)) .setIcon(KioskTranslator.getKioskIcon(ks, this)); kioskId++; } - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_rss)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_file_download)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_history)); //Settings and About - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_settings)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_info_outline)); - toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, - R.string.drawer_close); + toggle = new ActionBarDrawerToggle(this, mainBinding.getRoot(), + toolbarLayoutBinding.toolbar, R.string.drawer_open, R.string.drawer_close); toggle.syncState(); - drawer.addDrawerListener(toggle); - drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { + mainBinding.getRoot().addDrawerListener(toggle); + mainBinding.getRoot().addDrawerListener(new DrawerLayout.SimpleDrawerListener() { private int lastService; @Override @@ -218,12 +220,12 @@ public void onDrawerClosed(final View drawerView) { toggleServices(); } if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) { - new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate); + ActivityCompat.recreate(MainActivity.this); } } }); - drawerItems.setNavigationItemSelectedListener(this::drawerItemSelected); + drawerLayoutBinding.navigation.setNavigationItemSelectedListener(this::drawerItemSelected); setupDrawerHeader(); } @@ -246,15 +248,17 @@ private boolean drawerItemSelected(final MenuItem item) { return false; } - drawer.closeDrawers(); + mainBinding.getRoot().closeDrawers(); return true; } private void changeService(final MenuItem item) { - drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)) + drawerLayoutBinding.navigation.getMenu() + .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(false); ServiceHelper.setSelectedServiceId(this, item.getItemId()); - drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)) + drawerLayoutBinding.navigation.getMenu() + .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(true); } @@ -306,26 +310,19 @@ private void optionsAboutSelected(final MenuItem item) { } private void setupDrawerHeader() { - final NavigationView navigationView = findViewById(R.id.navigation); - final View hView = navigationView.getHeaderView(0); - - serviceArrow = hView.findViewById(R.id.drawer_arrow); - headerServiceIcon = hView.findViewById(R.id.drawer_header_service_icon); - headerServiceView = hView.findViewById(R.id.drawer_header_service_view); - toggleServiceButton = hView.findViewById(R.id.drawer_header_action_button); - toggleServiceButton.setOnClickListener(view -> toggleServices()); + drawerHeaderBinding.drawerHeaderActionButton.setOnClickListener(view -> toggleServices()); // If the current app name is bigger than the default "NewPipe" (7 chars), // let the text view grow a little more as well. if (getString(R.string.app_name).length() > "NewPipe".length()) { - final TextView headerTitle = hView.findViewById(R.id.drawer_header_newpipe_title); - final ViewGroup.LayoutParams layoutParams = headerTitle.getLayoutParams(); + final ViewGroup.LayoutParams layoutParams = + drawerHeaderBinding.drawerHeaderNewpipeTitle.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; - headerTitle.setLayoutParams(layoutParams); - headerTitle.setMaxLines(2); - headerTitle.setMinWidth(getResources() + drawerHeaderBinding.drawerHeaderNewpipeTitle.setLayoutParams(layoutParams); + drawerHeaderBinding.drawerHeaderNewpipeTitle.setMaxLines(2); + drawerHeaderBinding.drawerHeaderNewpipeTitle.setMinWidth(getResources() .getDimensionPixelSize(R.dimen.drawer_header_newpipe_title_default_width)); - headerTitle.setMaxWidth(getResources() + drawerHeaderBinding.drawerHeaderNewpipeTitle.setMaxWidth(getResources() .getDimensionPixelSize(R.dimen.drawer_header_newpipe_title_max_width)); } } @@ -333,9 +330,9 @@ private void setupDrawerHeader() { private void toggleServices() { servicesShown = !servicesShown; - drawerItems.getMenu().removeGroup(R.id.menu_services_group); - drawerItems.getMenu().removeGroup(R.id.menu_tabs_group); - drawerItems.getMenu().removeGroup(R.id.menu_options_about_group); + drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_services_group); + drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group); + drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group); if (servicesShown) { showServices(); @@ -349,13 +346,13 @@ private void toggleServices() { } private void showServices() { - serviceArrow.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp); + drawerHeaderBinding.drawerArrow.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp); for (final StreamingService s : NewPipe.getServices()) { final String title = s.getServiceInfo().getName() + (ServiceHelper.isBeta(s) ? " (beta)" : ""); - final MenuItem menuItem = drawerItems.getMenu() + final MenuItem menuItem = drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_services_group, s.getServiceId(), ORDER, title) .setIcon(ServiceHelper.getIcon(s.getServiceId())); @@ -364,21 +361,22 @@ private void showServices() { enhancePeertubeMenu(s, menuItem); } } - drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)) + drawerLayoutBinding.navigation.getMenu() + .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(true); } private void enhancePeertubeMenu(final StreamingService s, final MenuItem menuItem) { - final PeertubeInstance currentInstace = PeertubeHelper.getCurrentInstance(); - menuItem.setTitle(currentInstace.getName() + (ServiceHelper.isBeta(s) ? " (beta)" : "")); - final Spinner spinner = (Spinner) LayoutInflater.from(this) - .inflate(R.layout.instance_spinner_layout, null); + final PeertubeInstance currentInstance = PeertubeHelper.getCurrentInstance(); + menuItem.setTitle(currentInstance.getName() + (ServiceHelper.isBeta(s) ? " (beta)" : "")); + final Spinner spinner = InstanceSpinnerLayoutBinding.inflate(LayoutInflater.from(this)) + .getRoot(); final List instances = PeertubeHelper.getInstanceList(this); final List items = new ArrayList<>(); int defaultSelect = 0; for (final PeertubeInstance instance : instances) { items.add(instance.getName()); - if (instance.getUrl().equals(currentInstace.getUrl())) { + if (instance.getUrl().equals(currentInstance.getUrl())) { defaultSelect = items.size() - 1; } } @@ -397,7 +395,7 @@ public void onItemSelected(final AdapterView parent, final View view, } PeertubeHelper.selectInstance(newInstance, getApplicationContext()); changeService(menuItem); - drawer.closeDrawers(); + mainBinding.getRoot().closeDrawers(); new Handler(Looper.getMainLooper()).postDelayed(() -> { getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); @@ -414,7 +412,7 @@ public void onNothingSelected(final AdapterView parent) { } private void showTabs() throws ExtractionException { - serviceArrow.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp); + drawerHeaderBinding.drawerArrow.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp); //Tabs final int currentServiceId = ServiceHelper.getSelectedServiceId(this); @@ -423,34 +421,34 @@ private void showTabs() throws ExtractionException { int kioskId = 0; for (final String ks : service.getKioskList().getAvailableKiosks()) { - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, kioskId, ORDER, KioskTranslator.getTranslatedKioskName(ks, this)) .setIcon(KioskTranslator.getKioskIcon(ks, this)); kioskId++; } - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_rss)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_file_download)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_history)); //Settings and About - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_settings)); - drawerItems.getMenu() + drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about) .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_info_outline)); } @@ -475,16 +473,18 @@ protected void onResume() { // Close drawer on return, and don't show animation, // so it looks like the drawer isn't open when the user returns to MainActivity - drawer.closeDrawer(GravityCompat.START, false); + mainBinding.getRoot().closeDrawer(GravityCompat.START, false); try { final int selectedServiceId = ServiceHelper.getSelectedServiceId(this); final String selectedServiceName = NewPipe.getService(selectedServiceId) .getServiceInfo().getName(); - headerServiceView.setText(selectedServiceName); - headerServiceIcon.setImageResource(ServiceHelper.getIcon(selectedServiceId)); + drawerHeaderBinding.drawerHeaderServiceView.setText(selectedServiceName); + drawerHeaderBinding.drawerHeaderServiceIcon.setImageResource(ServiceHelper + .getIcon(selectedServiceId)); - headerServiceView.post(() -> headerServiceView.setSelected(true)); - toggleServiceButton.setContentDescription( + drawerHeaderBinding.drawerHeaderServiceView.post(() -> drawerHeaderBinding + .drawerHeaderServiceView.setSelected(true)); + drawerHeaderBinding.drawerHeaderActionButton.setContentDescription( getString(R.string.drawer_header_description) + selectedServiceName); } catch (final Exception e) { ErrorActivity.reportUiError(this, e); @@ -497,10 +497,7 @@ protected void onResume() { Log.d(TAG, "Theme has changed, recreating activity..."); } sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply(); - // https://stackoverflow.com/questions/10844112/ - // Briefly, let the activity resume - // properly posting the recreate call to end of the message queue - new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate); + ActivityCompat.recreate(this); } if (sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) { @@ -513,7 +510,8 @@ protected void onResume() { final boolean isHistoryEnabled = sharedPreferences.getBoolean( getString(R.string.enable_watch_history_key), true); - drawerItems.getMenu().findItem(ITEM_ID_HISTORY).setVisible(isHistoryEnabled); + drawerLayoutBinding.navigation.getMenu().findItem(ITEM_ID_HISTORY) + .setVisible(isHistoryEnabled); } @Override @@ -557,9 +555,8 @@ public void onBackPressed() { } if (DeviceUtils.isTv(this)) { - final View drawerPanel = findViewById(R.id.navigation); - if (drawer.isDrawerOpen(drawerPanel)) { - drawer.closeDrawers(); + if (mainBinding.getRoot().isDrawerOpen(drawerLayoutBinding.navigation)) { + mainBinding.getRoot().closeDrawers(); return; } } @@ -585,9 +582,7 @@ public void onBackPressed() { // delegate the back press to it if (fragmentPlayer instanceof BackPressable) { if (!((BackPressable) fragmentPlayer).onBackPressed()) { - final FrameLayout bottomSheetLayout = - findViewById(R.id.fragment_player_holder); - BottomSheetBehavior.from(bottomSheetLayout) + BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder) .setState(BottomSheetBehavior.STATE_COLLAPSED); } return; @@ -670,8 +665,7 @@ public boolean onCreateOptionsMenu(final Menu menu) { final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); if (!(fragment instanceof SearchFragment)) { - findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container) - .setVisibility(View.GONE); + toolbarLayoutBinding.toolbarSearchContainer.getRoot().setVisibility(View.GONE); } final ActionBar actionBar = getSupportActionBar(); @@ -732,21 +726,20 @@ private void updateDrawerNavigation() { return; } - final Toolbar toolbar = findViewById(R.id.toolbar); - final Fragment fragment = getSupportFragmentManager() .findFragmentById(R.id.fragment_holder); if (fragment instanceof MainFragment) { getSupportActionBar().setDisplayHomeAsUpEnabled(false); if (toggle != null) { toggle.syncState(); - toolbar.setNavigationOnClickListener(v -> drawer.openDrawer(GravityCompat.START)); - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED); + toolbarLayoutBinding.toolbar.setNavigationOnClickListener(v -> mainBinding.getRoot() + .openDrawer(GravityCompat.START)); + mainBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED); } } else { - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + mainBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - toolbar.setNavigationOnClickListener(v -> onHomeButtonPressed()); + toolbarLayoutBinding.toolbar.setNavigationOnClickListener(v -> onHomeButtonPressed()); } } @@ -854,9 +847,8 @@ public void onReceive(final Context context, final Intent intent) { } private boolean bottomSheetHiddenOrCollapsed() { - final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder); final BottomSheetBehavior bottomSheetBehavior = - BottomSheetBehavior.from(bottomSheetLayout); + BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder); final int sheetState = bottomSheetBehavior.getState(); return sheetState == BottomSheetBehavior.STATE_HIDDEN diff --git a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java index c962ed99dc4..463fc24acab 100644 --- a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java @@ -8,20 +8,18 @@ import android.view.Menu; import android.view.MenuItem; import android.webkit.CookieManager; -import android.webkit.WebResourceRequest; import android.webkit.WebSettings; import android.webkit.WebView; -import android.webkit.WebViewClient; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.core.app.NavUtils; import androidx.preference.PreferenceManager; +import androidx.webkit.WebViewClientCompat; +import org.schabi.newpipe.databinding.ActivityRecaptchaBinding; import org.schabi.newpipe.util.ThemeHelper; import java.io.UnsupportedEncodingException; @@ -53,46 +51,37 @@ public class ReCaptchaActivity extends AppCompatActivity { public static final String YT_URL = "https://www.youtube.com"; public static final String RECAPTCHA_COOKIES_KEY = "recaptcha_cookies"; - private WebView webView; + public static String sanitizeRecaptchaUrl(@Nullable final String url) { + if (url == null || url.trim().isEmpty()) { + return YT_URL; // YouTube is the most likely service to have thrown a recaptcha + } else { + // remove "pbj=1" parameter from YouYube urls, as it makes the page JSON and not HTML + return url.replace("&pbj=1", "").replace("pbj=1&", "").replace("?pbj=1", ""); + } + } + + private ActivityRecaptchaBinding recaptchaBinding; private String foundCookies = ""; @Override protected void onCreate(final Bundle savedInstanceState) { ThemeHelper.setTheme(this); super.onCreate(savedInstanceState); - setContentView(R.layout.activity_recaptcha); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - String url = getIntent().getStringExtra(RECAPTCHA_URL_EXTRA); - if (url == null || url.isEmpty()) { - url = YT_URL; - } + recaptchaBinding = ActivityRecaptchaBinding.inflate(getLayoutInflater()); + setContentView(recaptchaBinding.getRoot()); + setSupportActionBar(recaptchaBinding.toolbar); + final String url = sanitizeRecaptchaUrl(getIntent().getStringExtra(RECAPTCHA_URL_EXTRA)); // set return to Cancel by default setResult(RESULT_CANCELED); - - webView = findViewById(R.id.reCaptchaWebView); - // enable Javascript - final WebSettings webSettings = webView.getSettings(); + final WebSettings webSettings = recaptchaBinding.reCaptchaWebView.getSettings(); webSettings.setJavaScriptEnabled(true); + webSettings.setUserAgentString(DownloaderImpl.USER_AGENT); - webView.setWebViewClient(new WebViewClient() { - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean shouldOverrideUrlLoading(final WebView view, - final WebResourceRequest request) { - final String url = request.getUrl().toString(); - if (MainActivity.DEBUG) { - Log.d(TAG, "shouldOverrideUrlLoading: request.url=" + url); - } - - handleCookiesFromUrl(url); - return false; - } - + recaptchaBinding.reCaptchaWebView.setWebViewClient(new WebViewClientCompat() { @Override public boolean shouldOverrideUrlLoading(final WebView view, final String url) { if (MainActivity.DEBUG) { @@ -111,17 +100,16 @@ public void onPageFinished(final WebView view, final String url) { }); // cleaning cache, history and cookies from webView - webView.clearCache(true); - webView.clearHistory(); - final android.webkit.CookieManager cookieManager = CookieManager.getInstance(); + recaptchaBinding.reCaptchaWebView.clearCache(true); + recaptchaBinding.reCaptchaWebView.clearHistory(); + final CookieManager cookieManager = CookieManager.getInstance(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - cookieManager.removeAllCookies(aBoolean -> { - }); + cookieManager.removeAllCookies(value -> { }); } else { cookieManager.removeAllCookie(); } - webView.loadUrl(url); + recaptchaBinding.reCaptchaWebView.loadUrl(url); } @Override @@ -145,18 +133,16 @@ public void onBackPressed() { @Override public boolean onOptionsItemSelected(final MenuItem item) { - final int id = item.getItemId(); - switch (id) { - case R.id.menu_item_done: - saveCookiesAndFinish(); - return true; - default: - return false; + if (item.getItemId() == R.id.menu_item_done) { + saveCookiesAndFinish(); + return true; } + return false; } private void saveCookiesAndFinish() { - handleCookiesFromUrl(webView.getUrl()); // try to get cookies of unclosed page + // try to get cookies of unclosed page + handleCookiesFromUrl(recaptchaBinding.reCaptchaWebView.getUrl()); if (MainActivity.DEBUG) { Log.d(TAG, "saveCookiesAndFinish: foundCookies=" + foundCookies); } diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 9ad993de183..98a0921e4ea 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -14,7 +14,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; @@ -26,10 +25,13 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.app.NotificationCompat; +import androidx.core.app.ServiceCompat; import androidx.core.widget.TextViewCompat; import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; +import org.schabi.newpipe.databinding.ListRadioIconItemBinding; +import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.NewPipe; @@ -267,9 +269,8 @@ private void showDialog(final List choices) { final Context themeWrapperContext = getThemeWrapperContext(); final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext); - final LinearLayout rootLayout = (LinearLayout) inflater.inflate( - R.layout.single_choice_dialog_view, null, false); - final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list); + final RadioGroup radioGroup = SingleChoiceDialogViewBinding.inflate(getLayoutInflater()) + .list; final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> { final int indexOfChild = radioGroup.indexOfChild( @@ -322,8 +323,7 @@ private void showDialog(final List choices) { int id = 12345; for (final AdapterChoiceItem item : choices) { - final RadioButton radioButton - = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null); + final RadioButton radioButton = ListRadioIconItemBinding.inflate(inflater).getRoot(); radioButton.setText(item.description); TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(radioButton, AppCompatResources.getDrawable(getApplicationContext(), item.icon), @@ -696,7 +696,7 @@ public Consumer getResultHandler(final Choice choice) { @Override public void onDestroy() { super.onDestroy(); - stopForeground(true); + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE); if (fetcher != null) { fetcher.dispose(); } diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java index e3e56816c61..6ff69156112 100644 --- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java +++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java @@ -6,22 +6,19 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.adapter.FragmentStateAdapter; -import androidx.viewpager2.widget.ViewPager2; -import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.ActivityAboutBinding; +import org.schabi.newpipe.databinding.FragmentAboutBinding; import org.schabi.newpipe.util.ThemeHelper; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -68,40 +65,27 @@ public class AboutActivity extends AppCompatActivity { private static final int POS_ABOUT = 0; private static final int POS_LICENSE = 1; private static final int TOTAL_COUNT = 2; - /** - * The {@link RecyclerView.Adapter} that will provide - * fragments for each of the sections. We use a - * {@link FragmentStateAdapter} derivative, which will keep every - * loaded fragment in memory. - */ - private SectionsPagerAdapter mSectionsPagerAdapter; - /** - * The {@link ViewPager2} that will host the section contents. - */ - private ViewPager2 mViewPager; @Override protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); ThemeHelper.setTheme(this); - this.setTitle(getString(R.string.title_activity_about)); + setTitle(getString(R.string.title_activity_about)); - setContentView(R.layout.activity_about); + final ActivityAboutBinding aboutBinding = ActivityAboutBinding.inflate(getLayoutInflater()); + setContentView(aboutBinding.getRoot()); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + setSupportActionBar(aboutBinding.toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Create the adapter that will return a fragment for each of the three // primary sections of the activity. - mSectionsPagerAdapter = new SectionsPagerAdapter(this); + final SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(this); // Set up the ViewPager with the sections adapter. - mViewPager = findViewById(R.id.container); - mViewPager.setAdapter(mSectionsPagerAdapter); + aboutBinding.container.setAdapter(mSectionsPagerAdapter); - final TabLayout tabLayout = findViewById(R.id.tabs); - new TabLayoutMediator(tabLayout, mViewPager, (tab, position) -> { + new TabLayoutMediator(aboutBinding.tabs, aboutBinding.container, (tab, position) -> { switch (position) { default: case POS_ABOUT: @@ -143,33 +127,28 @@ public static AboutFragment newInstance() { } @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { - final View rootView = inflater.inflate(R.layout.fragment_about, container, false); - final Context context = this.getContext(); + final FragmentAboutBinding aboutBinding = + FragmentAboutBinding.inflate(inflater, container, false); + final Context context = getContext(); - final TextView version = rootView.findViewById(R.id.app_version); - version.setText(BuildConfig.VERSION_NAME); + aboutBinding.appVersion.setText(BuildConfig.VERSION_NAME); - final View githubLink = rootView.findViewById(R.id.github_link); - githubLink.setOnClickListener(nv -> + aboutBinding.githubLink.setOnClickListener(nv -> openUrlInBrowser(context, context.getString(R.string.github_url))); - final View donationLink = rootView.findViewById(R.id.donation_link); - donationLink.setOnClickListener(v -> + aboutBinding.donationLink.setOnClickListener(v -> openUrlInBrowser(context, context.getString(R.string.donation_url))); - final View websiteLink = rootView.findViewById(R.id.website_link); - websiteLink.setOnClickListener(nv -> + aboutBinding.websiteLink.setOnClickListener(nv -> openUrlInBrowser(context, context.getString(R.string.website_url))); - final View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link); - privacyPolicyLink.setOnClickListener(v -> + aboutBinding.privacyPolicyLink.setOnClickListener(v -> openUrlInBrowser(context, context.getString(R.string.privacy_policy_url))); - return rootView; + return aboutBinding.getRoot(); } - } /** diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt index d9c892099aa..aff6205f27e 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt @@ -9,7 +9,7 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem import kotlin.jvm.Throws -class PlaylistStreamEntry( +data class PlaylistStreamEntry( @Embedded val streamEntity: StreamEntity, diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java index 979f8be7533..37eefed96c6 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java @@ -9,10 +9,10 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentTransaction; import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.ActivityDownloaderBinding; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; @@ -35,11 +35,14 @@ protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); ThemeHelper.setTheme(this); + super.onCreate(savedInstanceState); - setContentView(R.layout.activity_downloader); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + final ActivityDownloaderBinding downloaderBinding = + ActivityDownloaderBinding.inflate(getLayoutInflater()); + setContentView(downloaderBinding.getRoot()); + + setSupportActionBar(downloaderBinding.toolbarLayout.toolbar); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 866b324ecbe..a77109f86f2 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -3,7 +3,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.os.Bundle; -import androidx.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -19,6 +18,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround; +import androidx.preference.PreferenceManager; import androidx.viewpager.widget.ViewPager; import com.google.android.material.tabs.TabLayout; 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 427cff06ee2..6560ab404d2 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 @@ -16,7 +16,6 @@ import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.text.TextUtils; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Log; @@ -122,12 +121,14 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; +import static android.text.TextUtils.isEmpty; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public final class VideoDetailFragment extends BaseStateFragment @@ -218,6 +219,9 @@ public final class VideoDetailFragment private TextView detailDurationView; private TextView detailPositionView; + private View detailMetaInfoSeparator; + private TextView detailMetaInfoTextView; + private LinearLayout videoDescriptionRootLayout; private TextView videoUploadDateView; private TextView videoDescriptionView; @@ -508,8 +512,8 @@ public void onClick(final View v) { } break; case R.id.detail_uploader_root_layout: - if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) { - if (!TextUtils.isEmpty(currentInfo.getUploaderUrl())) { + if (isEmpty(currentInfo.getSubChannelUrl())) { + if (!isEmpty(currentInfo.getUploaderUrl())) { openChannel(currentInfo.getUploaderUrl(), currentInfo.getUploaderName()); } @@ -583,7 +587,7 @@ public boolean onLongClick(final View v) { } break; case R.id.detail_uploader_root_layout: - if (TextUtils.isEmpty(currentInfo.getSubChannelUrl())) { + if (isEmpty(currentInfo.getSubChannelUrl())) { Log.w(TAG, "Can't open parent channel because we got no parent channel URL"); } else { @@ -644,6 +648,9 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) { detailDurationView = rootView.findViewById(R.id.detail_duration_view); detailPositionView = rootView.findViewById(R.id.detail_position_view); + detailMetaInfoSeparator = rootView.findViewById(R.id.detail_meta_info_separator); + detailMetaInfoTextView = rootView.findViewById(R.id.detail_meta_info_text_view); + videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout); videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view); videoDescriptionView = rootView.findViewById(R.id.detail_description_view); @@ -748,7 +755,7 @@ private View.OnTouchListener getOnControlsTouchListener() { private void initThumbnailViews(@NonNull final StreamInfo info) { thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); - if (!TextUtils.isEmpty(info.getThumbnailUrl())) { + if (!isEmpty(info.getThumbnailUrl())) { final String infoServiceName = NewPipe.getNameOfService(info.getServiceId()); final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() { @Override @@ -763,12 +770,12 @@ public void onLoadingFailed(final String imageUri, final View view, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, onFailListener); } - if (!TextUtils.isEmpty(info.getSubChannelAvatarUrl())) { + if (!isEmpty(info.getSubChannelAvatarUrl())) { IMAGE_LOADER.displayImage(info.getSubChannelAvatarUrl(), subChannelThumb, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); } - if (!TextUtils.isEmpty(info.getUploaderAvatarUrl())) { + if (!isEmpty(info.getUploaderAvatarUrl())) { IMAGE_LOADER.displayImage(info.getUploaderAvatarUrl(), uploaderThumb, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); } @@ -1217,7 +1224,7 @@ private void makeDefaultHeightForVideoPlaceholder() { } private void prepareDescription(final Description description) { - if (description == null || TextUtils.isEmpty(description.getContent()) + if (description == null || isEmpty(description.getContent()) || description == Description.emptyDescription) { return; } @@ -1462,9 +1469,9 @@ public void handleResult(@NonNull final StreamInfo info) { animateView(thumbnailPlayButton, true, 200); videoTitleTextView.setText(title); - if (!TextUtils.isEmpty(info.getSubChannelName())) { + if (!isEmpty(info.getSubChannelName())) { displayBothUploaderAndSubChannel(info); - } else if (!TextUtils.isEmpty(info.getUploaderName())) { + } else if (!isEmpty(info.getUploaderName())) { displayUploaderAsSubChannel(info); } else { uploaderTextView.setVisibility(View.GONE); @@ -1559,6 +1566,8 @@ public void handleResult(@NonNull final StreamInfo info) { prepareDescription(info.getDescription()); updateProgressInfo(info); initThumbnailViews(info); + showMetaInfoInTextView(info.getMetaInfo(), detailMetaInfoTextView, detailMetaInfoSeparator); + if (player == null || player.isPlayerStopped()) { updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); @@ -1610,7 +1619,7 @@ private void displayBothUploaderAndSubChannel(final StreamInfo info) { subChannelThumb.setVisibility(View.VISIBLE); - if (!TextUtils.isEmpty(info.getUploaderName())) { + if (!isEmpty(info.getUploaderName())) { uploaderTextView.setText( String.format(getString(R.string.video_detail_by), info.getUploaderName())); uploaderTextView.setVisibility(View.VISIBLE); @@ -2305,10 +2314,10 @@ public void onSlide(@NonNull final View bottomSheet, final float slideOffset) { private void updateOverlayData(@Nullable final String overlayTitle, @Nullable final String uploader, @Nullable final String thumbnailUrl) { - overlayTitleTextView.setText(TextUtils.isEmpty(overlayTitle) ? "" : overlayTitle); - overlayChannelTextView.setText(TextUtils.isEmpty(uploader) ? "" : uploader); + overlayTitleTextView.setText(isEmpty(title) ? "" : title); + overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); overlayThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); - if (!TextUtils.isEmpty(thumbnailUrl)) { + if (!isEmpty(thumbnailUrl)) { IMAGE_LOADER.displayImage(thumbnailUrl, overlayThumbnailImageView, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index d1a964fb2b5..6fa7eb700b9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -11,12 +11,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.content.res.AppCompatResources; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -26,8 +26,10 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; +import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; @@ -44,13 +46,13 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.StreamDialogEntry; -import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import de.hdodenhof.circleimageview.CircleImageView; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.core.Single; @@ -58,6 +60,7 @@ import io.reactivex.rxjava3.disposables.Disposable; import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr; public class PlaylistFragment extends BaseListInfoFragment { private CompositeDisposable disposables; @@ -74,7 +77,7 @@ public class PlaylistFragment extends BaseListInfoFragment { private TextView headerTitleView; private View headerUploaderLayout; private TextView headerUploaderName; - private ImageView headerUploaderAvatar; + private CircleImageView headerUploaderAvatar; private TextView headerStreamCount; private View playlistCtrl; @@ -301,8 +304,22 @@ public void handleResult(@NonNull final PlaylistInfo result) { playlistCtrl.setVisibility(View.VISIBLE); - IMAGE_LOADER.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); + final String avatarUrl = result.getUploaderAvatarUrl(); + if (result.getServiceId() == ServiceList.YouTube.getServiceId() + && (YoutubeParsingHelper.isYoutubeMixId(result.getId()) + || YoutubeParsingHelper.isYoutubeMusicMixId(result.getId()))) { + // this is an auto-generated playlist (e.g. Youtube mix), so a radio is shown + headerUploaderAvatar.setDisableCircularTransformation(true); + headerUploaderAvatar.setBorderColor( + getResources().getColor(R.color.transparent_background_color)); + headerUploaderAvatar.setImageDrawable(AppCompatResources.getDrawable(requireContext(), + resolveResourceIdFromAttr(requireContext(), R.attr.ic_radio))); + + } else { + IMAGE_LOADER.displayImage(avatarUrl, headerUploaderAvatar, + ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); + } + headerStreamCount.setText(Localization .localizeStreamCount(getContext(), result.getStreamCount())); @@ -476,7 +493,7 @@ private void updateBookmarkButtons() { final int titleRes = playlistEntity == null ? R.string.bookmark_playlist : R.string.unbookmark_playlist; - playlistBookmarkButton.setIcon(ThemeHelper.resolveResourceIdFromAttr(activity, iconAttr)); + playlistBookmarkButton.setIcon(resolveResourceIdFromAttr(activity, iconAttr)); playlistBookmarkButton.setTitle(titleRes); } } 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 02dbf176bc5..2dac6d11b38 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 @@ -39,6 +39,7 @@ import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; +import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; @@ -79,6 +80,7 @@ import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static java.util.Arrays.asList; import static org.schabi.newpipe.util.AnimationUtils.animateView; +import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; public class SearchFragment extends BaseListFragment> implements BackPressable { @@ -129,6 +131,9 @@ public class SearchFragment extends BaseListFragment cannot be bundled without creating some containers + metaInfo = new MetaInfo[result.getMetaInfo().size()]; + metaInfo = result.getMetaInfo().toArray(metaInfo); + handleSearchSuggestion(); + showMetaInfoInTextView(result.getMetaInfo(), metaInfoTextView, metaInfoSeparator); + lastSearchedString = searchString; nextPage = result.getNextPage(); diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt index ddbbea23dfe..2a0aa1c90fd 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt @@ -30,6 +30,7 @@ import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.app.ServiceCompat import androidx.preference.PreferenceManager import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Flowable @@ -147,7 +148,7 @@ class FeedLoadService : Service() { private fun stopService() { disposeAll() - stopForeground(true) + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) notificationManager.cancel(NOTIFICATION_ID) stopSelf() } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java index 73c0d23a031..34543b56587 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java @@ -31,6 +31,7 @@ import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.app.ServiceCompat; import org.reactivestreams.Publisher; import org.schabi.newpipe.R; @@ -162,7 +163,7 @@ protected void stopAndReportError(@Nullable final Throwable error, final String protected void postErrorResult(final String title, final String text) { disposeAll(); - stopForeground(true); + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE); stopSelf(); if (title == null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java index 860ace84c31..c1c2e4eba87 100644 --- a/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/NotificationUtil.java @@ -15,6 +15,7 @@ import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; import org.schabi.newpipe.MainActivity; @@ -188,7 +189,7 @@ void createNotificationAndStartForeground(final VideoPlayerImpl player, final Se } void cancelNotificationAndStopForeground(final Service service) { - service.stopForeground(true); + ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_REMOVE); if (notificationManager != null) { notificationManager.cancel(NOTIFICATION_ID); diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index d1c2be014eb..fd20fd175b2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.player; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; @@ -11,15 +12,11 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.LinearLayout; import android.widget.PopupMenu; -import android.widget.ProgressBar; import android.widget.SeekBar; -import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -28,6 +25,7 @@ import com.google.android.exoplayer2.Player; import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.ActivityPlayerQueueControlBinding; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; @@ -69,30 +67,10 @@ public abstract class ServicePlayerActivity extends AppCompatActivity // Views //////////////////////////////////////////////////////////////////////////// - private View rootView; + private ActivityPlayerQueueControlBinding queueControlBinding; - private RecyclerView itemsList; private ItemTouchHelper itemTouchHelper; - private LinearLayout metadata; - private TextView metadataTitle; - private TextView metadataArtist; - - private SeekBar progressSeekBar; - private TextView progressCurrentTime; - private TextView progressEndTime; - private TextView progressLiveSync; - private TextView seekDisplay; - - private ImageButton repeatButton; - private ImageButton backwardButton; - private ImageButton fastRewindButton; - private ImageButton playPauseButton; - private ImageButton fastForwardButton; - private ImageButton forwardButton; - private ImageButton shuffleButton; - private ProgressBar progressBar; - private Menu menu; //////////////////////////////////////////////////////////////////////////// @@ -122,11 +100,11 @@ protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); ThemeHelper.setTheme(this); - setContentView(R.layout.activity_player_queue_control); - rootView = findViewById(R.id.main_content); - final Toolbar toolbar = rootView.findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + queueControlBinding = ActivityPlayerQueueControlBinding.inflate(getLayoutInflater()); + setContentView(queueControlBinding.getRoot()); + + setSupportActionBar(queueControlBinding.toolbar); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(getSupportActionTitle()); @@ -140,7 +118,7 @@ protected void onCreate(final Bundle savedInstanceState) { protected void onResume() { super.onResume(); if (redraw) { - recreate(); + ActivityCompat.recreate(this); redraw = false; } } @@ -229,14 +207,11 @@ private void unbind() { if (player != null && player.getPlayQueueAdapter() != null) { player.getPlayQueueAdapter().unsetSelectedListener(); } - if (itemsList != null) { - itemsList.setAdapter(null); - } + queueControlBinding.playQueue.setAdapter(null); if (itemTouchHelper != null) { itemTouchHelper.attachToRecyclerView(null); } - itemsList = null; itemTouchHelper = null; player = null; } @@ -283,58 +258,38 @@ private void buildComponents() { } private void buildQueue() { - itemsList = findViewById(R.id.play_queue); - itemsList.setLayoutManager(new LinearLayoutManager(this)); - itemsList.setAdapter(player.getPlayQueueAdapter()); - itemsList.setClickable(true); - itemsList.setLongClickable(true); - itemsList.clearOnScrollListeners(); - itemsList.addOnScrollListener(getQueueScrollListener()); + queueControlBinding.playQueue.setLayoutManager(new LinearLayoutManager(this)); + queueControlBinding.playQueue.setAdapter(player.getPlayQueueAdapter()); + queueControlBinding.playQueue.setClickable(true); + queueControlBinding.playQueue.setLongClickable(true); + queueControlBinding.playQueue.clearOnScrollListeners(); + queueControlBinding.playQueue.addOnScrollListener(getQueueScrollListener()); itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); - itemTouchHelper.attachToRecyclerView(itemsList); + itemTouchHelper.attachToRecyclerView(queueControlBinding.playQueue); player.getPlayQueueAdapter().setSelectedListener(getOnSelectedListener()); } private void buildMetadata() { - metadata = rootView.findViewById(R.id.metadata); - metadataTitle = rootView.findViewById(R.id.song_name); - metadataArtist = rootView.findViewById(R.id.artist_name); - - metadata.setOnClickListener(this); - metadataTitle.setSelected(true); - metadataArtist.setSelected(true); + queueControlBinding.metadata.setOnClickListener(this); + queueControlBinding.songName.setSelected(true); + queueControlBinding.artistName.setSelected(true); } private void buildSeekBar() { - progressCurrentTime = rootView.findViewById(R.id.current_time); - progressSeekBar = rootView.findViewById(R.id.seek_bar); - progressEndTime = rootView.findViewById(R.id.end_time); - progressLiveSync = rootView.findViewById(R.id.live_sync); - seekDisplay = rootView.findViewById(R.id.seek_display); - - progressSeekBar.setOnSeekBarChangeListener(this); - progressLiveSync.setOnClickListener(this); + queueControlBinding.seekBar.setOnSeekBarChangeListener(this); + queueControlBinding.liveSync.setOnClickListener(this); } private void buildControls() { - repeatButton = rootView.findViewById(R.id.control_repeat); - backwardButton = rootView.findViewById(R.id.control_backward); - fastRewindButton = rootView.findViewById(R.id.control_fast_rewind); - playPauseButton = rootView.findViewById(R.id.control_play_pause); - fastForwardButton = rootView.findViewById(R.id.control_fast_forward); - forwardButton = rootView.findViewById(R.id.control_forward); - shuffleButton = rootView.findViewById(R.id.control_shuffle); - progressBar = rootView.findViewById(R.id.control_progress_bar); - - repeatButton.setOnClickListener(this); - backwardButton.setOnClickListener(this); - fastRewindButton.setOnClickListener(this); - playPauseButton.setOnClickListener(this); - fastForwardButton.setOnClickListener(this); - forwardButton.setOnClickListener(this); - shuffleButton.setOnClickListener(this); + queueControlBinding.controlRepeat.setOnClickListener(this); + queueControlBinding.controlBackward.setOnClickListener(this); + queueControlBinding.controlFastRewind.setOnClickListener(this); + queueControlBinding.controlPlayPause.setOnClickListener(this); + queueControlBinding.controlFastForward.setOnClickListener(this); + queueControlBinding.controlForward.setOnClickListener(this); + queueControlBinding.controlShuffle.setOnClickListener(this); } private void buildItemPopupMenu(final PlayQueueItem item, final View view) { @@ -390,8 +345,8 @@ public void onScrolledDown(final RecyclerView recyclerView) { if (player != null && player.getPlayQueue() != null && !player.getPlayQueue().isComplete()) { player.getPlayQueue().fetch(); - } else if (itemsList != null) { - itemsList.clearOnScrollListeners(); + } else { + queueControlBinding.playQueue.clearOnScrollListeners(); } } }; @@ -452,8 +407,9 @@ private void scrollToSelected() { final int currentPlayingIndex = player.getPlayQueue().getIndex(); final int currentVisibleIndex; - if (itemsList.getLayoutManager() instanceof LinearLayoutManager) { - final LinearLayoutManager layout = ((LinearLayoutManager) itemsList.getLayoutManager()); + if (queueControlBinding.playQueue.getLayoutManager() instanceof LinearLayoutManager) { + final LinearLayoutManager layout = + (LinearLayoutManager) queueControlBinding.playQueue.getLayoutManager(); currentVisibleIndex = layout.findFirstVisibleItemPosition(); } else { currentVisibleIndex = 0; @@ -461,9 +417,9 @@ private void scrollToSelected() { final int distance = Math.abs(currentPlayingIndex - currentVisibleIndex); if (distance < SMOOTH_SCROLL_MAXIMUM_DISTANCE) { - itemsList.smoothScrollToPosition(currentPlayingIndex); + queueControlBinding.playQueue.smoothScrollToPosition(currentPlayingIndex); } else { - itemsList.scrollToPosition(currentPlayingIndex); + queueControlBinding.playQueue.scrollToPosition(currentPlayingIndex); } } @@ -477,23 +433,23 @@ public void onClick(final View view) { return; } - if (view.getId() == repeatButton.getId()) { + if (view.getId() == queueControlBinding.controlRepeat.getId()) { player.onRepeatClicked(); - } else if (view.getId() == backwardButton.getId()) { + } else if (view.getId() == queueControlBinding.controlBackward.getId()) { player.onPlayPrevious(); - } else if (view.getId() == fastRewindButton.getId()) { + } else if (view.getId() == queueControlBinding.controlFastRewind.getId()) { player.onFastRewind(); - } else if (view.getId() == playPauseButton.getId()) { + } else if (view.getId() == queueControlBinding.controlPlayPause.getId()) { player.onPlayPause(); - } else if (view.getId() == fastForwardButton.getId()) { + } else if (view.getId() == queueControlBinding.controlFastForward.getId()) { player.onFastForward(); - } else if (view.getId() == forwardButton.getId()) { + } else if (view.getId() == queueControlBinding.controlForward.getId()) { player.onPlayNext(); - } else if (view.getId() == shuffleButton.getId()) { + } else if (view.getId() == queueControlBinding.controlShuffle.getId()) { player.onShuffleClicked(); - } else if (view.getId() == metadata.getId()) { + } else if (view.getId() == queueControlBinding.metadata.getId()) { scrollToSelected(); - } else if (view.getId() == progressLiveSync.getId()) { + } else if (view.getId() == queueControlBinding.liveSync.getId()) { player.seekToDefault(); } } @@ -527,15 +483,15 @@ public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) { if (fromUser) { final String seekTime = Localization.getDurationString(progress / 1000); - progressCurrentTime.setText(seekTime); - seekDisplay.setText(seekTime); + queueControlBinding.currentTime.setText(seekTime); + queueControlBinding.seekDisplay.setText(seekTime); } } @Override public void onStartTrackingTouch(final SeekBar seekBar) { seeking = true; - seekDisplay.setVisibility(View.VISIBLE); + queueControlBinding.seekDisplay.setVisibility(View.VISIBLE); } @Override @@ -543,7 +499,7 @@ public void onStopTrackingTouch(final SeekBar seekBar) { if (player != null) { player.seekTo(seekBar.getProgress()); } - seekDisplay.setVisibility(View.GONE); + queueControlBinding.seekDisplay.setVisibility(View.GONE); seeking = false; } @@ -601,45 +557,46 @@ public void onPlaybackUpdate(final int state, final int repeatMode, final boolea public void onProgressUpdate(final int currentProgress, final int duration, final int bufferPercent) { // Set buffer progress - progressSeekBar.setSecondaryProgress((int) (progressSeekBar.getMax() + queueControlBinding.seekBar.setSecondaryProgress((int) (queueControlBinding.seekBar.getMax() * ((float) bufferPercent / 100))); // Set Duration - progressSeekBar.setMax(duration); - progressEndTime.setText(Localization.getDurationString(duration / 1000)); + queueControlBinding.seekBar.setMax(duration); + queueControlBinding.endTime.setText(Localization.getDurationString(duration / 1000)); // Set current time if not seeking if (!seeking) { - progressSeekBar.setProgress(currentProgress); - progressCurrentTime.setText(Localization.getDurationString(currentProgress / 1000)); + queueControlBinding.seekBar.setProgress(currentProgress); + queueControlBinding.currentTime.setText(Localization + .getDurationString(currentProgress / 1000)); } if (player != null) { - progressLiveSync.setClickable(!player.isLiveEdge()); + queueControlBinding.liveSync.setClickable(!player.isLiveEdge()); } // this will make sure progressCurrentTime has the same width as progressEndTime - final ViewGroup.LayoutParams endTimeParams = progressEndTime.getLayoutParams(); - final ViewGroup.LayoutParams currentTimeParams = progressCurrentTime.getLayoutParams(); - currentTimeParams.width = progressEndTime.getWidth(); - progressCurrentTime.setLayoutParams(currentTimeParams); + final ViewGroup.LayoutParams currentTimeParams = + queueControlBinding.currentTime.getLayoutParams(); + currentTimeParams.width = queueControlBinding.endTime.getWidth(); + queueControlBinding.currentTime.setLayoutParams(currentTimeParams); } @Override public void onMetadataUpdate(final StreamInfo info, final PlayQueue queue) { if (info != null) { - metadataTitle.setText(info.getName()); - metadataArtist.setText(info.getUploaderName()); + queueControlBinding.songName.setText(info.getName()); + queueControlBinding.artistName.setText(info.getUploaderName()); - progressEndTime.setVisibility(View.GONE); - progressLiveSync.setVisibility(View.GONE); + queueControlBinding.endTime.setVisibility(View.GONE); + queueControlBinding.liveSync.setVisibility(View.GONE); switch (info.getStreamType()) { case LIVE_STREAM: case AUDIO_LIVE_STREAM: - progressLiveSync.setVisibility(View.VISIBLE); + queueControlBinding.liveSync.setVisibility(View.VISIBLE); break; default: - progressEndTime.setVisibility(View.VISIBLE); + queueControlBinding.endTime.setVisibility(View.VISIBLE); break; } @@ -660,13 +617,16 @@ public void onServiceStopped() { private void onStateChanged(final int state) { switch (state) { case BasePlayer.STATE_PAUSED: - playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp); + queueControlBinding.controlPlayPause + .setImageResource(R.drawable.ic_play_arrow_white_24dp); break; case BasePlayer.STATE_PLAYING: - playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp); + queueControlBinding.controlPlayPause + .setImageResource(R.drawable.ic_pause_white_24dp); break; case BasePlayer.STATE_COMPLETED: - playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp); + queueControlBinding.controlPlayPause + .setImageResource(R.drawable.ic_replay_white_24dp); break; default: break; @@ -676,14 +636,14 @@ private void onStateChanged(final int state) { case BasePlayer.STATE_PAUSED: case BasePlayer.STATE_PLAYING: case BasePlayer.STATE_COMPLETED: - playPauseButton.setClickable(true); - playPauseButton.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); + queueControlBinding.controlPlayPause.setClickable(true); + queueControlBinding.controlPlayPause.setVisibility(View.VISIBLE); + queueControlBinding.controlProgressBar.setVisibility(View.GONE); break; default: - playPauseButton.setClickable(false); - playPauseButton.setVisibility(View.INVISIBLE); - progressBar.setVisibility(View.VISIBLE); + queueControlBinding.controlPlayPause.setClickable(false); + queueControlBinding.controlPlayPause.setVisibility(View.INVISIBLE); + queueControlBinding.controlProgressBar.setVisibility(View.VISIBLE); break; } } @@ -691,18 +651,21 @@ private void onStateChanged(final int state) { private void onPlayModeChanged(final int repeatMode, final boolean shuffled) { switch (repeatMode) { case Player.REPEAT_MODE_OFF: - repeatButton.setImageResource(R.drawable.exo_controls_repeat_off); + queueControlBinding.controlRepeat + .setImageResource(R.drawable.exo_controls_repeat_off); break; case Player.REPEAT_MODE_ONE: - repeatButton.setImageResource(R.drawable.exo_controls_repeat_one); + queueControlBinding.controlRepeat + .setImageResource(R.drawable.exo_controls_repeat_one); break; case Player.REPEAT_MODE_ALL: - repeatButton.setImageResource(R.drawable.exo_controls_repeat_all); + queueControlBinding.controlRepeat + .setImageResource(R.drawable.exo_controls_repeat_all); break; } final int shuffleAlpha = shuffled ? 255 : 77; - shuffleButton.setImageAlpha(shuffleAlpha); + queueControlBinding.controlShuffle.setImageAlpha(shuffleAlpha); } private void onPlaybackParameterChanged(final PlaybackParameters parameters) { @@ -715,12 +678,13 @@ private void onPlaybackParameterChanged(final PlaybackParameters parameters) { } private void onMaybePlaybackAdapterChanged() { - if (itemsList == null || player == null) { + if (player == null) { return; } final PlayQueueAdapter maybeNewAdapter = player.getPlayQueueAdapter(); - if (maybeNewAdapter != null && itemsList.getAdapter() != maybeNewAdapter) { - itemsList.setAdapter(maybeNewAdapter); + if (maybeNewAdapter != null + && queueControlBinding.playQueue.getAdapter() != maybeNewAdapter) { + queueControlBinding.playQueue.setAdapter(maybeNewAdapter); } } @@ -734,7 +698,8 @@ private void onMaybeMuteChanged() { //2) Icon change accordingly to current App Theme // using rootView.getContext() because getApplicationContext() didn't work - item.setIcon(ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), + final Context context = queueControlBinding.getRoot().getContext(); + item.setIcon(ThemeHelper.resolveResourceIdFromAttr(context, player.isMuted() ? R.attr.ic_volume_off : R.attr.ic_volume_up)); diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 3cbcb87a35b..a304b44300b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -103,6 +103,7 @@ import java.util.List; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.player.MainPlayer.ACTION_CLOSE; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD; import static org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND; @@ -889,10 +890,17 @@ private void onMoreOptionsClicked() { private void onShareClicked() { // share video at the current time (youtube.com/watch?v=ID&t=SECONDS) // Timestamp doesn't make sense in a live stream so drop it - final String ts = isLive() ? "" : ("&t=" + (getPlaybackSeekBar().getProgress() / 1000)); + + final int ts = getPlaybackSeekBar().getProgress() / 1000; + final MediaSourceTag metadata = getCurrentMetadata(); + String videoUrl = getVideoUrl(); + if (!isLive() && ts >= 0 && metadata != null + && metadata.getMetadata().getServiceId() == YouTube.getServiceId()) { + videoUrl += ("&t=" + ts); + } ShareUtils.shareUrl(service, getVideoTitle(), - getVideoUrl() + ts); + videoUrl); } private void onPlayWithKodiClicked() { diff --git a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java index 3213821cd3c..a4b6af2ab01 100644 --- a/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/report/ErrorActivity.java @@ -14,15 +14,11 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.core.app.NavUtils; import com.google.android.material.snackbar.Snackbar; @@ -34,6 +30,7 @@ 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; import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ThemeHelper; @@ -87,7 +84,8 @@ public class ErrorActivity extends AppCompatActivity { private ErrorInfo errorInfo; private Class returnActivity; private String currentTimeStamp; - private EditText userCommentBox; + + private ActivityErrorBinding activityErrorBinding; public static void reportUiError(final AppCompatActivity activity, final Throwable el) { reportError(activity, el, activity.getClass(), null, ErrorInfo.make(UserAction.UI_ERROR, @@ -181,12 +179,13 @@ protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); ThemeHelper.setTheme(this); - setContentView(R.layout.activity_error); + + activityErrorBinding = ActivityErrorBinding.inflate(getLayoutInflater()); + setContentView(activityErrorBinding.getRoot()); final Intent intent = getIntent(); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + setSupportActionBar(activityErrorBinding.toolbarLayout.toolbar); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { @@ -195,15 +194,6 @@ protected void onCreate(final Bundle savedInstanceState) { actionBar.setDisplayShowTitleEnabled(true); } - final Button reportEmailButton = findViewById(R.id.errorReportEmailButton); - final Button copyButton = findViewById(R.id.errorReportCopyButton); - final Button reportGithubButton = findViewById(R.id.errorReportGitHubButton); - - userCommentBox = findViewById(R.id.errorCommentBox); - final TextView errorView = findViewById(R.id.errorView); - final TextView infoView = findViewById(R.id.errorInfosView); - final TextView errorMessageView = findViewById(R.id.errorMessageView); - final ActivityCommunicator ac = ActivityCommunicator.getCommunicator(); returnActivity = ac.getReturnActivity(); errorInfo = intent.getParcelableExtra(ERROR_INFO); @@ -213,28 +203,27 @@ protected void onCreate(final Bundle savedInstanceState) { addGuruMeditation(); currentTimeStamp = getCurrentTimeStamp(); - reportEmailButton.setOnClickListener(v -> + activityErrorBinding.errorReportEmailButton.setOnClickListener(v -> openPrivacyPolicyDialog(this, "EMAIL")); - copyButton.setOnClickListener(v -> { + activityErrorBinding.errorReportCopyButton.setOnClickListener(v -> { ShareUtils.copyToClipboard(this, buildMarkdown()); Toast.makeText(this, R.string.msg_copied, Toast.LENGTH_SHORT).show(); }); - reportGithubButton.setOnClickListener(v -> + activityErrorBinding.errorReportGitHubButton.setOnClickListener(v -> openPrivacyPolicyDialog(this, "GITHUB")); - // normal bugreport buildInfo(errorInfo); if (errorInfo.getMessage() != 0) { - errorMessageView.setText(errorInfo.getMessage()); + activityErrorBinding.errorMessageView.setText(errorInfo.getMessage()); } else { - errorMessageView.setVisibility(View.GONE); - findViewById(R.id.messageWhatHappenedView).setVisibility(View.GONE); + activityErrorBinding.errorMessageView.setVisibility(View.GONE); + activityErrorBinding.messageWhatHappenedView.setVisibility(View.GONE); } - errorView.setText(formErrorText(errorList)); + activityErrorBinding.errorView.setText(formErrorText(errorList)); // print stack trace once again for debugging: for (final String e : errorList) { @@ -339,11 +328,10 @@ private void goToReturnActivity() { } private void buildInfo(final ErrorInfo info) { - final TextView infoLabelView = findViewById(R.id.errorInfoLabelsView); - final TextView infoView = findViewById(R.id.errorInfosView); String text = ""; - infoLabelView.setText(getString(R.string.info_labels).replace("\\n", "\n")); + activityErrorBinding.errorInfoLabelsView.setText(getString(R.string.info_labels) + .replace("\\n", "\n")); text += getUserActionString(info.getUserAction()) + "\n" + info.getRequest() + "\n" @@ -356,7 +344,7 @@ private void buildInfo(final ErrorInfo info) { + BuildConfig.VERSION_NAME + "\n" + getOsString(); - infoView.setText(text); + activityErrorBinding.errorInfosView.setText(text); } private String buildJson() { @@ -374,7 +362,8 @@ private String buildJson() { .value("os", getOsString()) .value("time", currentTimeStamp) .array("exceptions", Arrays.asList(errorList)) - .value("user_comment", userCommentBox.getText().toString()) + .value("user_comment", activityErrorBinding.errorCommentBox.getText() + .toString()) .end() .done(); } catch (final Throwable e) { @@ -389,7 +378,7 @@ private String buildMarkdown() { try { final StringBuilder htmlErrorReport = new StringBuilder(); - final String userComment = userCommentBox.getText().toString(); + final String userComment = activityErrorBinding.errorCommentBox.getText().toString(); if (!userComment.isEmpty()) { htmlErrorReport.append(userComment).append("\n"); } @@ -473,10 +462,9 @@ private String getOsString() { private void addGuruMeditation() { //just an easter egg - final TextView sorryView = findViewById(R.id.errorSorryView); - String text = sorryView.getText().toString(); + String text = activityErrorBinding.errorSorryView.getText().toString(); text += "\n" + getString(R.string.guru_meditation); - sorryView.setText(text); + activityErrorBinding.errorSorryView.setText(text); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index ab875ed5d32..8126bd2c56b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -8,6 +8,7 @@ import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; import androidx.preference.Preference; import org.schabi.newpipe.R; @@ -31,7 +32,7 @@ public boolean onPreferenceChange(final Preference preference, final Object newV if (!newValue.equals(startThemeKey) && getActivity() != null) { // If it's not the current theme - getActivity().recreate(); + ActivityCompat.recreate(requireActivity()); } return false; diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index b0425ebfaba..d7766f7b091 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -11,6 +11,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.preference.Preference; import androidx.preference.PreferenceManager; @@ -30,19 +31,15 @@ import org.schabi.newpipe.util.FilePickerActivityHelper; import org.schabi.newpipe.util.ZipHelper; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Map; import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -50,6 +47,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment { private static final int REQUEST_IMPORT_PATH = 8945; private static final int REQUEST_EXPORT_PATH = 30945; + private ContentSettingsManager manager; + private File databasesDir; private File newpipeDb; private File newpipeDbJournal; @@ -120,17 +119,18 @@ public boolean onPreferenceTreeClick(final Preference preference) { @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { - - final String homeDir = getActivity().getApplicationInfo().dataDir; - databasesDir = new File(homeDir + "/databases"); - newpipeDb = new File(homeDir + "/databases/newpipe.db"); - newpipeDbJournal = new File(homeDir + "/databases/newpipe.db-journal"); - newpipeDbShm = new File(homeDir + "/databases/newpipe.db-shm"); - newpipeDbWal = new File(homeDir + "/databases/newpipe.db-wal"); - - newpipeSettings = new File(homeDir + "/databases/newpipe.settings"); + final File homeDir = ContextCompat.getDataDir(requireContext()); + databasesDir = new File(homeDir, "/databases"); + newpipeDb = new File(homeDir, "/databases/newpipe.db"); + newpipeDbJournal = new File(homeDir, "/databases/newpipe.db-journal"); + newpipeDbShm = new File(homeDir, "/databases/newpipe.db-shm"); + newpipeDbWal = new File(homeDir, "/databases/newpipe.db-wal"); + + newpipeSettings = new File(homeDir, "/databases/newpipe.settings"); newpipeSettings.delete(); + manager = new ContentSettingsManager(homeDir); + addPreferencesFromResource(R.xml.content_settings); final Preference importDataPreference = findPreference(getString(R.string.import_data)); @@ -212,33 +212,16 @@ private void exportDatabase(final String path) { //checkpoint before export NewPipeDatabase.checkpoint(); - try (ZipOutputStream outZip = new ZipOutputStream(new BufferedOutputStream( - new FileOutputStream(path)))) { - ZipHelper.addFileToZip(outZip, newpipeDb.getPath(), "newpipe.db"); - - saveSharedPreferencesToFile(newpipeSettings); - ZipHelper.addFileToZip(outZip, newpipeSettings.getPath(), - "newpipe.settings"); - } + final SharedPreferences preferences = PreferenceManager + .getDefaultSharedPreferences(requireContext()); + manager.exportDatabase(preferences, path); - Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT) - .show(); + Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT).show(); } catch (final Exception e) { onError(e); } } - private void saveSharedPreferencesToFile(final File dst) { - try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(dst))) { - final SharedPreferences pref - = PreferenceManager.getDefaultSharedPreferences(requireContext()); - output.writeObject(pref.getAll()); - output.flush(); - } catch (final IOException e) { - e.printStackTrace(); - } - } - private void importDatabase(final String filePath) { // check if file is supported try (ZipFile zipFile = new ZipFile(filePath)) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt new file mode 100644 index 00000000000..2682ac5e0a6 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsManager.kt @@ -0,0 +1,45 @@ +package org.schabi.newpipe.settings + +import android.content.SharedPreferences +import org.schabi.newpipe.util.ZipHelper +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.ObjectOutputStream +import java.lang.Exception +import java.util.zip.ZipOutputStream + +class ContentSettingsManager( + private val newpipeDb: File, + private val newpipeSettings: File +) { + + constructor(homeDir: File) : this( + File(homeDir, "databases/newpipe.db"), + File(homeDir, "databases/newpipe.settings") + ) + + /** + * Exports given [SharedPreferences] to the file in given outputPath. + * It also creates the file. + */ + @Throws(Exception::class) + fun exportDatabase(preferences: SharedPreferences, outputPath: String) { + ZipOutputStream(BufferedOutputStream(FileOutputStream(outputPath))) + .use { outZip -> + ZipHelper.addFileToZip(outZip, newpipeDb.path, "newpipe.db") + + try { + ObjectOutputStream(FileOutputStream(newpipeSettings)).use { output -> + output.writeObject(preferences.all) + output.flush() + } + } catch (e: IOException) { + e.printStackTrace() + } + + ZipHelper.addFileToZip(outZip, newpipeSettings.path, "newpipe.settings") + } + } +} 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 d2d4c240439..4de166a5599 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -7,12 +7,12 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import org.schabi.newpipe.R; +import org.schabi.newpipe.databinding.SettingsLayoutBinding; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; @@ -51,10 +51,12 @@ protected void onCreate(final Bundle savedInstanceBundle) { setTheme(ThemeHelper.getSettingsThemeStyle(this)); assureCorrectAppLanguage(this); super.onCreate(savedInstanceBundle); - setContentView(R.layout.settings_layout); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + final SettingsLayoutBinding settingsLayoutBinding = + SettingsLayoutBinding.inflate(getLayoutInflater()); + setContentView(settingsLayoutBinding.getRoot()); + + setSupportActionBar(settingsLayoutBinding.toolbarLayout.toolbar); if (savedInstanceBundle == null) { getSupportFragmentManager().beginTransaction() diff --git a/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java b/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java index 5efffe118bc..ca3da9d2449 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java +++ b/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java @@ -483,7 +483,7 @@ private void initChunkTables(final TablesInfo tables, final int firstCount, // stsc_table_entry = [first_chunk, samples_per_chunk, sample_description_index] tables.stscBEntries = new int[tables.stsc * 3]; - tables.stco = remainChunkOffset + 1; // total entrys in chunk offset box + tables.stco = remainChunkOffset + 1; // total entries in chunk offset box tables.stscBEntries[index++] = 1; tables.stscBEntries[index++] = firstCount; diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 650c5ae116d..1f1b945452a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -22,9 +22,16 @@ import android.content.Context; import android.content.Intent; import android.os.Handler; +import android.text.method.LinkMovementMethod; import android.util.Log; +import android.view.View; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.core.text.HtmlCompat; +import androidx.preference.PreferenceManager; + import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.ReCaptchaActivity; @@ -32,6 +39,7 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.ListInfo; +import org.schabi.newpipe.extractor.MetaInfo; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; @@ -60,6 +68,8 @@ import io.reactivex.rxjava3.core.Maybe; import io.reactivex.rxjava3.core.Single; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + public final class ExtractorHelper { private static final String TAG = ExtractorHelper.class.getSimpleName(); private static final InfoCache CACHE = InfoCache.getInstance(); @@ -306,4 +316,73 @@ public static void handleGeneralException(final Context context, final int servi } }); } + + /** + * Formats the text contained in the meta info list as HTML and puts it into the text view, + * while also making the separator visible. If the list is null or empty, or the user chose not + * to see meta information, both the text view and the separator are hidden + * @param metaInfos a list of meta information, can be null or empty + * @param metaInfoTextView the text view in which to show the formatted HTML + * @param metaInfoSeparator another view to be shown or hidden accordingly to the text view + */ + public static void showMetaInfoInTextView(@Nullable final List metaInfos, + final TextView metaInfoTextView, + final View metaInfoSeparator) { + final Context context = metaInfoTextView.getContext(); + final boolean showMetaInfo = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.show_meta_info_key), true); + + if (!showMetaInfo || metaInfos == null || metaInfos.isEmpty()) { + metaInfoTextView.setVisibility(View.GONE); + metaInfoSeparator.setVisibility(View.GONE); + + } else { + final StringBuilder stringBuilder = new StringBuilder(); + for (final MetaInfo metaInfo : metaInfos) { + if (!isNullOrEmpty(metaInfo.getTitle())) { + stringBuilder.append("").append(metaInfo.getTitle()).append("") + .append(Localization.DOT_SEPARATOR); + } + + String content = metaInfo.getContent().getContent().trim(); + if (content.endsWith(".")) { + content = content.substring(0, content.length() - 1); // remove . at end + } + stringBuilder.append(content); + + for (int i = 0; i < metaInfo.getUrls().size(); i++) { + if (i == 0) { + stringBuilder.append(Localization.DOT_SEPARATOR); + } else { + stringBuilder.append("

"); + } + + stringBuilder + .append("") + .append(capitalizeIfAllUppercase(metaInfo.getUrlTexts().get(i).trim())) + .append(""); + } + } + + metaInfoTextView.setText(HtmlCompat.fromHtml(stringBuilder.toString(), + HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING)); + metaInfoTextView.setMovementMethod(LinkMovementMethod.getInstance()); + metaInfoTextView.setVisibility(View.VISIBLE); + metaInfoSeparator.setVisibility(View.VISIBLE); + } + } + + private static String capitalizeIfAllUppercase(final String text) { + for (int i = 0; i < text.length(); i++) { + if (Character.isLowerCase(text.charAt(i))) { + return text; // there is at least a lowercase letter -> not all uppercase + } + } + + if (text.isEmpty()) { + return text; + } else { + return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 71082786492..978f558c46a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -57,7 +57,7 @@ public final class Localization { - private static final String DOT_SEPARATOR = " • "; + public static final String DOT_SEPARATOR = " • "; private static PrettyTime prettyTime; private Localization() { } diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java index b43733a5140..e7719644581 100755 --- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java +++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java @@ -25,6 +25,7 @@ import android.os.Message; import android.os.Parcelable; +import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import android.util.Log; @@ -235,7 +236,7 @@ public void onDestroy() { Log.d(TAG, "Destroying"); } - stopForeground(true); + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE); if (mNotificationManager != null && downloadDoneNotification != null) { downloadDoneNotification.setDeleteIntent(null);// prevent NewPipe running when is killed, cleared from recent, etc @@ -363,7 +364,7 @@ public void updateForegroundState(boolean state) { if (state) { startForeground(FOREGROUND_NOTIFICATION_ID, mNotification); } else { - stopForeground(true); + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE); } manageLock(state); diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index be7d78299cb..f102206c132 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -1,7 +1,7 @@ package us.shandian.giga.ui.adapter; import android.annotation.SuppressLint; -import android.app.ProgressDialog; +import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.graphics.Color; @@ -26,6 +26,8 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.DiffUtil; @@ -91,6 +93,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb private static final String DEFAULT_MIME_TYPE = "*/*"; private static final String UNDEFINED_ETA = "--:--"; + private static final int HASH_NOTIFICATION_ID = 123790; static { ALGORITHMS.put(R.id.md5, "MD5"); @@ -678,28 +681,28 @@ private boolean handlePopupItem(@NonNull ViewHolderItem h, @NonNull MenuItem opt return true; case R.id.md5: case R.id.sha1: - ProgressDialog progressDialog = null; - if (mContext != null) { - // Create dialog - progressDialog = new ProgressDialog(mContext); - progressDialog.setCancelable(false); - progressDialog.setMessage(mContext.getString(R.string.msg_wait)); - progressDialog.show(); - } - final ProgressDialog finalProgressDialog = progressDialog; + final NotificationManager notificationManager + = ContextCompat.getSystemService(mContext, NotificationManager.class); + final NotificationCompat.Builder progressNotificationBuilder + = new NotificationCompat.Builder(mContext, + mContext.getString(R.string.hash_channel_id)) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setSmallIcon(R.drawable.ic_newpipe_triangle_white) + .setContentTitle(mContext.getString(R.string.msg_calculating_hash)) + .setContentText(mContext.getString(R.string.msg_wait)) + .setProgress(0, 0, true) + .setOngoing(true); + + notificationManager.notify(HASH_NOTIFICATION_ID, progressNotificationBuilder + .build()); final StoredFileHelper storage = h.item.mission.storage; compositeDisposable.add( Observable.fromCallable(() -> Utility.checksum(storage, ALGORITHMS.get(id))) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - if (finalProgressDialog != null) { - Utility.copyToClipboard(finalProgressDialog.getContext(), - result); - if (mContext != null) { - finalProgressDialog.dismiss(); - } - } + Utility.copyToClipboard(mContext, result); + notificationManager.cancel(HASH_NOTIFICATION_ID); }) ); return true; diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml index 13e3c1b5fac..d90c782ef24 100644 --- a/app/src/main/res/layout-large-land/fragment_video_detail.xml +++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml @@ -2,12 +2,12 @@ + + + + - + - + @@ -15,7 +14,9 @@ android:layout_height="match_parent" android:layout_marginTop="?attr/actionBarSize" /> - + - + diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index ed651b17a8e..f52d0d996fa 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -11,15 +11,34 @@ android:layout_height="wrap_content" android:layout_alignTop="@id/error_panel" android:background="?attr/selectableItemBackground" - android:padding="10dp" + android:padding="12dp" android:textSize="@dimen/search_suggestion_text_size" tools:text="Showing results for lorem ipsum dolor sit amet consectetur adipisci elit" /> + + + + diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 0df85fe959e..758a88f1993 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -491,6 +491,23 @@ + + + + @@ -63,8 +63,8 @@ android:id="@+id/anchorRight" android:layout_width="1dp" android:layout_height="match_parent" - android:layout_marginTop="@dimen/playlist_ctrl_seperator_margin" - android:layout_marginBottom="@dimen/playlist_ctrl_seperator_margin" + android:layout_marginTop="@dimen/playlist_ctrl_separator_margin" + android:layout_marginBottom="@dimen/playlist_ctrl_separator_margin" android:background="?attr/colorAccent" android:clickable="false" /> diff --git a/app/src/main/res/layout/settings_layout.xml b/app/src/main/res/layout/settings_layout.xml index d50924c46ad..32c6c6b91cf 100644 --- a/app/src/main/res/layout/settings_layout.xml +++ b/app/src/main/res/layout/settings_layout.xml @@ -12,6 +12,8 @@ android:layout_height="match_parent" android:layout_marginTop="?attr/actionBarSize" /> - + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 14bbb79a9f4..efd682d9ed4 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -47,13 +47,13 @@ رابط غير مدعوم استخدام مشغل صوت خارجي استخدام مشغل فيديو خارجي - (إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ). + (إختبارية) إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية (تشغيل الفيديو المباشر غير مدعوم حتى الأن). استخدام تور %1$s مشاهدة محتوى غير متوفر - تعذرت عملية تحميل كافة صور المعاينة + تعذر تحميل كافة الصور المصغرة خطأ - تعذرت عملية تحليل الموقع + تعذر تحليل الموقع تعذر فك تشفير توقيع رابط الفيديو اضغط على \"بحث\" للبدء \n @@ -102,7 +102,7 @@ استئناف التشغيل متابعة التشغيل بعد المقاطعات (مثل المكالمات الهاتفية) إظهار التلميحات \"اضغط للتجاهل\" - عرض تلميح عند الضغط على الخلفية أو الزر المنبثق في الفيديو \"التفاصيل:\" + عرض تلميح على صفحة التفاصيل عند استخدام وضع مشغل الخلفية أو النافذة المنبثقة المشغل السلوك الوضع المنبثق @@ -127,10 +127,10 @@ تنبيهات مشغل NewPipe للخلفية والنوافذ المنبثقة [غير معروف] لا يمكن تحليل الموقع بشكل كلي - تعذرت عملية إعداد قائمة التنزيل - البث المباشر غير مدعوم حتى الآن + تعذر إعداد قائمة التنزيل + البثوث المباشرة ليست مدعومةً بعد تعذر الحصول على أي بث - تعذرت عملية تحميل الصورة + تعذر تحميل الصورة تعطل التطبيق / واجهة المستخدم لا يمكن تشغيل هذا البث حدث خطأ للمشغل غير قابل للاسترداد @@ -223,11 +223,11 @@ اختر قناة لم يتم الاشتراك في أي قناة بعد الشائعة - أفضل 50 + أفضل ٥٠ جديد وساخن حذف التفاصيل - الإعدادات الصوتية + إعدادات الصوت تشغيل هنا بدأ التشغيل في نافذة منبثقة تحدي الكابتشا @@ -282,19 +282,19 @@ إلغاء إعادة التسمية تمت عملية التصدير - إكتَملَت عملية الإستيراد - تنبيه : تعذرت عملية استيراد كافة الملفات. + تمَّت عملية الإستيراد + تنبيه: تعذر استيراد كافة الملفات. سوف يظهر شيء هنا قريبا ;D مشغل الفيديو السؤال دائماً - الحصول على المعلومات … + الحصول على المعلومات… تحميل المحتوى المطلوب إنشاء قائمة تشغيل جديدة حذف قائمة التشغيل إعادة تسمية التسمية إضافة إلى قائمة تشغيل - هل تريد حذف قائمة التشغيل هذه ؟ + هل تريد حذف قائمة التشغيل هذه؟ تم إنشاء قائمة التشغيل تمت إضافتها إلى قائمة التشغيل لا يمكن حذف قائمة التشغيل. @@ -308,7 +308,7 @@ تم إفراغ مساحة ذاكرة التخزين المؤقتة الخاصة بالصور الملف لا يوجد مثل هذا المجلد - لا يمكن أن يكون اسم الملف فارغا + لا يمكن أن يكون اسم الملف فارغًا طرأ هناك خطأ: %1$s ملف مضغوط ZIP غير صالح إزالة الفواصل المرجعية @@ -327,7 +327,7 @@ إزالة جميع بيانات صفحات الويب المخزنة مؤقتًا تم محو ذاكرة التخزين المؤقت للبيانات الوصفية وضع البث القادم تلقائيا في قائمة الإنتظار - استمر في إنهاء قائمة انتظار التشغيل (الغير المتكررة) من خلال إلحاق بث ذي صلة + استمر في إنهاء قائمة انتظار التشغيل (الغير المتكررة) من خلال إلحاق التدفق المرتبط إضافة صورة مصغرة إلى قائمة التشغيل تفضيل قائمة التشغيل تم تغيير الصورة المصغرة لقائمة التشغيل. @@ -338,12 +338,12 @@ تقرير الأخطاء خارج دورة الحياة فرض الإبلاغ عن استثناءات Rx غير القابلة للتسليم خارج دورة حياة الجزء أو النشاط بعد التخلص منها محو سجل المشاهدة - حذف محفوظات التدفقات التي تم تشغيلها ومواقف التشغيل - حذف سجل المشاهدة بالكامل\? + حذف محفوظات البثوث التي تم تشغيلها ومواقف التشغيل + حذف سجل المشاهدة بالكامل؟ تم حذف سجل المشاهدة. محو سجل البحث يحذف تاريخ البحث عن الكلمات الرئيسية - حذف سِجل البحث بالكامل\? + حذف سِجل البحث بالكامل؟ تم حذف سجل البحث. المشغل الخارجي لا يدعم هذه الأنواع من الروابط لا يوجد مثل هذا الملف/مصدر المحتوى @@ -460,15 +460,15 @@ فشل الاتصال الآمن تعذر العثور على الخادم لا يمكن الاتصال بالخادم - الخادم لايقوم بإرسال البيانات + الخادم لا يقوم بإرسال البيانات الخادم لا يقبل التنزيل المتعدد، إعادة المحاولة مع @string/msg_threads = 1 غير موجود فشلت المعالجة الاولية توقف - أقصى عدد للمحاولات - الحد الأقصى لعدد محاولات قبل إلغاء التحميل + عدد المحاولات الأقصى + الحد الأقصى لعدد المحاولات قبل إلغاء التحميل المقاطعة على الشبكات المقيسة - مفيد عند التبديل إلى بيانات الجوال ، على الرغم من أنه لا يمكن تعليق بعض التنزيلات + مفيد عند التبديل إلى بيانات الجوال، ولكن لا يمكن تعليق بعض التنزيلات إظهار التعليقات عطّله لإخفاء التعليقات تشغيل تلقائي @@ -498,7 +498,7 @@ سيُطلب منك مكان حفظ كل تنزيل سيطلب منك مكان حفظ كل تنزيل. \nاختر SAF إذا كنت تريد التنزيل على بطاقة SD خارجية - استخدام آمن + استخدام SAF يسمح \"إطار الوصول إلى التخزين\" بالتنزيل على بطاقة SD خارجية. \nبعض الأجهزة غير متوافقة حذف مواقف التشغيل @@ -535,16 +535,16 @@ يتم دعم عناوين URL HTTPS فقط مثيل الخادم موجود بالفعل محلي - أضيف مؤخرا - الأكثر إعجابا + أُضيف مؤخرًا + الأكثر إعجابًا تم إنشاؤه-تلقائيًا (لم يتم العثور على برنامج تحميل) استرد لا يمكن استرداد هذا التنزيل اختيار مثيل ابحث عن مثيلات الخوادم التي تناسبك على %s تنظيف تاريخ التحميل - حذف الملفات التي تم تنزيلها - التنزيلات %1$d المحذوفة + حذف الملفات المحملة + تم حذف %1$d من التحميلات إعطاء إذن لعرضه على التطبيقات الأخرى لغة التطبيق النظام الافتراضي @@ -572,14 +572,14 @@ \nلذا فإن الاختيار يتلخص في ما تفضله: السرعة أو المعلومات الدقيقة.
تعطيل الوضع السريع تمكين الوضع السريع - متوفر في بعض الخدمات ، وعادة ما يكون أسرع بكثير ولكن قد يُرجع كمية محدودة من العناصر وغالبًا معلومات غير مكتملة (على سبيل المثال ، بدون مدة أو نوع عنصر أو حالة مباشرة). + متوفر في بعض الخدمات، وعادةً ما يكون أسرع بكثير ولكن قد يُرجع كمية محدودة من العناصر وغالبًا ما تكون معلومات غير مكتملة (مثلًا بدون مدة أو نوع عنصر أو حالة مباشرة). جلب من تغذية مخصصة عندما تكون متاحة - تحديث دائما + تحديث دائمًا الوقت بعد التحديث الأخير قبل اعتبار الاشتراك قديمًا — %s عتبة تحديث التغذية تغذية جديد - هل تريد حذف هذه المجموعة\? + هل تريد حذف هذه المجموعة؟ اسم المجموعة فارغ %d تحديد @@ -637,9 +637,9 @@ \nقم بتشغيل \"%1$s\" في الإعدادات إذا كنت تريد رؤيته. نعم، ومقاطع الفيديو التي تمت مشاهدتها جزئيًا ستتم إزالة مقاطع الفيديو التي تمت مشاهدتها قبل وبعد إضافتها إلى قائمة التشغيل. -\nهل أنت واثق؟ هذا لا يمكن التراجع عنها! +\nهل أنت واثق؟ لا يمكن التراجع عن هذا! إزالة مقاطع الفيديو التي تمت مشاهدتها؟ - إزالة ماتمت مشاهدته + إزالة ما تمت مشاهدته ستكون النصوص الأصلية من الخدمات مرئية في عناصر البث عرض الوقت الأصلي على العناصر شغيل \"وضع تقييد المحتوى\" في يوتيوب @@ -649,7 +649,7 @@ صفحة قائمة التشغيل إظهار الاشتراكات غير المجمعة فقط لا توجد إشارات مرجعية لقائمة التشغيل حتى الآن - حدد قائمة تشغيل + اختر قائمة تشغيل يرجى التحقق مما إذا كانت هناك مشكلة في مناقشة تعطلك بالفعل. عند إنشاء تذاكر مكررة ، ستأخذ وقتًا منا يمكن أن نقضيه في إصلاح الخطأ الفعلي. تقرير على جيثب نسخ تقرير منسق diff --git a/app/src/main/res/values-b+zh+HANS+CN/strings.xml b/app/src/main/res/values-b+zh+HANS+CN/strings.xml index 8076d279f0a..be4aa932e50 100644 --- a/app/src/main/res/values-b+zh+HANS+CN/strings.xml +++ b/app/src/main/res/values-b+zh+HANS+CN/strings.xml @@ -5,7 +5,7 @@ %1$s 次观看 发布于 %1$s 在浏览器中打开 - 在悬浮窗模式下打开 + 在悬浮窗中打开 您是不是要找:%1$s? 找不到串流播放器 (您可以安装 VLC 进行播放)。 下载串流文件 @@ -18,16 +18,16 @@ 分享给... 选择浏览器 视频下载路径 - 已下载的视频存储在这里 + 已下载的视频将存储于此 请选择下载视频的保存位置 - 已下载的音频存储在这里 + 已下载的音频将存储于此 选择下载音频的储存位置 自动播放 使用Kodi播放 主题 浅色 - 暗黑 - 黑色 + 深色 + 暗黑 下载 不支持的 URL 外观 @@ -38,16 +38,17 @@ 稍后 网络错误 - %s 视频 + %s 部视频 + 禁用 后台播放 过滤器 刷新 - 搜索建议 + 显示搜索建议 订阅 已订阅 - 观看历史 + 观看记录 播放器 历史记录与缓存 播放列表 @@ -57,11 +58,11 @@ 仅一次 添加至 文件 - 加载缩略图 - 清除观看记录 + 加载封面 + 清空观看记录 - 最小化后台播放器 - 最小化悬浮窗播放器 + 最小化至后台播放 + 最小化至悬浮窗播放 频道 播放列表 取消订阅 @@ -72,22 +73,22 @@ 发布新版本时,通知我升级应用 网格 NewPipe可更新! - 服务器不接受 接收 multi-threaded 下载, 以 @string/msg_threads = 1 重试 - 自动播放 - 清除数据 + 服务器不接受多线程下载, 使用 @string/msg_threads = 1 重试 + 自动恢复上次播放 + 清空数据 观看记录已删除 喜欢 不喜欢 使用Tor (实验性)通过 Tor 强制播放来增强隐私(暂不支持串流视频)。 - 报告错误 - 用户报告 + 反馈错误 + 用户反馈 无法创建下载目录\"%1$s\" 已成功创建下载目录「%1$s」 视频 音频 重试 - 存储访问权限已被拒绝 + 访问存储权限已被拒绝 %s 次观看 @@ -113,7 +114,7 @@ 设置 关于 第三方许可 - © %1$s :作者 %2$s (使用 %3$s ) + © %1$s :开发者 %2$s (使用 %3$s 许可证) 无法加载许可证 打开网站 关于 @@ -121,34 +122,34 @@ 许可证 下载 文件名中允许的字符 - 无效字符将会被替换为此 + 无效字符将会被替换为该字符 字母和数字 - 最特殊字符 + 特殊字符 没有结果 - 没有订阅者 + 无订阅者 %s 位订阅者 - 没有视频 + 无视频 拖动以重新排序 - 创建 - 仅删除一个 - 全部删除 - 解除 + 新建 + 仅删除一条 + 删除全部 + 退出 重命名 未安装可播放此文件的应用程序 - 已删除1个项目。 - 哪些标签需要在主页上展示 + 已删除一项。 + 自定义主页显示的标签页 列表视图模式 已完成 等待中… 已暂停 - 排队中 + 已加入队列 加入队列 操作已被系统拒绝 下载失败 下载完成 - %s 次下载已完成 + %s 个下载已完成 没有评论 切换服务,当前选择: 找不到串流播放器。您想安装 VLC 吗? @@ -158,20 +159,21 @@ 音频下载文件夹 从其他应用调用 NewPipe 时播放视频 默认分辨率 - 找不到Kore。是否安装? - 显示“用Kodi播放”选项 + 找不到Kore +\n是否安装Kore? + 显示“使用Kodi播放”选项 显示“通过Kodi media center播放视频”的选项 音频 默认音频格式 - 显示“下一个”和“相似”视频 + 显示“接下来”和“类似视频” 视频和音频 - 在后台播放 + 后台播放 内容 - 展示年龄限制的内容 + 显示年龄限制的内容 直播 下载 下载 - 错误报告 + 反馈错误 错误 无法加载所有缩略图 无法解密视频的 URL 签名 @@ -182,46 +184,46 @@ 暂时不支持观看直播 无法获得任何串流 无法加载图像 - App UI 崩溃 - 抱歉,这本不应该发生。 - 使用电子邮件报告错误 + App/UI 崩溃 + 抱歉,这本不该发生。 + 使用电子邮件反馈错误 抱歉,发生了一些错误。 - 报告 + 反馈 信息: 发生了什么: 详情:\\n请求:\\n内容语言:\\n内容国家:\\n客户端语言:\\n服务:\\nGMT时间:\\n包名:\\n版本:\\n操作系统版本: - 您的附近说明(请用英文): + 您的附加说明(请用英文): 详细信息: 视频预览缩略图 - 播放视频,时长: + 播放视频,时长: 视频上传者的头像缩略图 - 字节 + 十亿 错误的 URL 或未联网 - NewPipe下载中 + NewPipe正在下载文件 请稍后在设置中设定下载目录 - 用悬浮窗模式 -\n需要此权限 + 使用悬浮窗模式 +\n需要该权限 reCAPTCHA验证 - 请求的新的CAPTCHA验证 + 已请求新的CAPTCHA验证 NewPipe 悬浮窗模式 在悬浮窗中播放 - 默认悬浮窗分辨率 + 默认分辨率(悬浮窗模式) 使用更高的分辨率 仅某些设备支持播放2K / 4K视频 清除 记住悬浮窗属性 - 记住最后一次使用悬浮窗的大小和位置 + 记住上一次使用悬浮窗的大小和位置 悬浮窗 调整大小 - 隐藏部分没有音频的分辨率 - 播放器手势控制 + 在部分分辨率下将没有音频 + 手势控制播放器 允许使用手势控制亮度和音量 - 显示搜索建议 + 搜索时显示搜索建议 最佳分辨率 - 开源小巧的Android媒体播放器。 + 开源且小巧的Android媒体播放器。 在GitHub上查看 NewPipe开源许可证 - 你是否有想:翻译、设计、清理或重构代码更改 ——我们始终欢迎你来贡献! + 你是否想过要翻译、设计、清理或重构代码 ——我们始终欢迎你来贡献! 阅读许可证 贡献 替换字符 @@ -231,31 +233,31 @@ 主页 订阅 最新 - 恢复前台焦点 - 中断后继续播放(例如突然来电后) - 搜索历史记录 - 在本地存储搜索查询记录 - 记录已观看视频 - 历史 + 自动恢复播放 + 在播放被打断(例如突然来电)后恢复播放 + 搜索记录 + 存储本地搜索记录 + 保留观看记录 + 历史记录 已搜索 已观看 历史记录功能已关闭 - 历史 - 历史记录为空 - 清除历史记录 + 历史记录 + 尚无历史记录 + 历史记录已清空 NewPipe 通知 - NewPipe 后台播放和悬浮窗播放的通知 + NewPipe 在后台播放和悬浮窗播放时在通知栏中显示通知 默认视频格式 行为 空空如也... - 0次观看 + 无人观看过 项目已删除 - 是否要从搜索历史记录中删除此项目? - 显示在主页面内容 + 是否删除此条搜索记录? + 主页面的显示内容 空白页 『时下流行』页-自定义 订阅页 - Feed 页面 + Feed 页 频道页 选择一个频道 尚未订阅频道 @@ -265,16 +267,16 @@ 前50 最新与热门 显示 \"长按添加\" 说明 - 在视频详情页中,按下背景播放或悬浮窗播放按钮时显示提示 + 在视频详情页中,长按背景播放或悬浮窗播放按钮时显示提示 已加入后台播放播放列表 已加入悬浮窗播放列表 无法播放此串流 - 发生无法恢复播放器错误 - 恢复播放器错误 + 发生无法处理的播放器错误 + 播放器错误 自动恢复 移除 详情 音频设置 - 长按队列 + 长按加入播放列表 [未知] 开始在此处开始播放 开始后台播放 @@ -282,11 +284,11 @@ 捐赠 NewPipe是由志愿者开发的,他们利用自己的空闲时间为您带来最佳的用户体验。在开发者享受一杯咖啡的时候,回报他们,帮助他们让NewPipe变得更好。 反馈 - 网站 + 官网 请访问 NewPipe 网站了解更多信息和讯息。 - 默认国家/地区 + 视频默认国家/地区 切换方向 - 切换到背景播放 + 切换到后台播放 切换到悬浮窗播放 切换到主页面 服务 @@ -303,7 +305,7 @@ 正在加载请求的内容 导入数据库 导出数据库 - 覆盖您的当前播放历史、订阅、播放列表和(可选)设置 + 覆盖您的当前播放历史、订阅、播放列表和设置(可选) 导出历史记录、订阅、播放列表和设置 导出成功 导入成功 @@ -311,94 +313,95 @@ 警告:无法导入所有文件。 此操作会覆盖当前设置 显示信息 - 书签 - 确定要从观看历史记录中删除该项吗? - 是否确实要从历史记录中删除所有项目? - 最后播放 - 播放最多 - 总是询问 + 收藏 + 是否删除该条搜索记录吗? + 是否确定删除所有历史记录? + 最近观看 + 最多观看 + 每次询问 新建播放列表 删除 重命名 名称 添加到播放列表 - 设为播放列表缩略图 + 设为播放列表封面 收藏播放列表 删除收藏 - 删除此播放列表? + 是否删除此播放列表? 新建播放列表成功 加入播放列表成功 - 播放列表缩略图更改成功。 + 播放列表封面更改成功。 无法删除播放列表 无字幕 适应屏幕 填充屏幕 - 缩放 + 缩放画面 敬请期待 调试 自动生成 『内存泄漏监视』可能导致应用在『核心转储』时无响应 报告『提前结束Android生命周期』错误 强制报告处理后的未送达的Activity或Fragment生命周期之外的Rx异常 - 使用快速不精确搜索 - 粗略定位播放:允许播放器以略低的精确度为代价换取更快的定位速度。此功能不适用于每隔5、15或25秒定位. - 自动播放下一个 - 当播放完非循环列表中的最后一个视频时,自动加入一个相关视频到播放列表 - 没有此文件夹 + 使用快速寻址(不精确) + 快速寻址定位允许播放器以较低精确度为代价换取更快的寻址定位速度。此功能不适用于以5、15或25秒为隔的寻址定位. + 自动将“接下来”视频加入播放列表 + 播放完(非循环)列表中的最后一个视频后,自动将一个相关视频添加到当前播放列表 + 没有该文件夹 无相似文件/内容源 - 该文件不存在 或 缺少读写该文件的权限 + 该文件不存在 或 缺少读写文件权限 文件名不能为空 发生错误: %1$s 导入/导出 导入 - 从...导入 + 导入自… 导出到... 正在导入… 正在导出… 导入文件 - 以前的导出 + 之前的导出 无法导入订阅 无法导出订阅 从 Google takeout 导入YouTube 订阅: \n -\n1. 转到这个URL:%1$s -\n2. 登录谷歌账户 -\n3. 点击“所有包含的数据”,然后点击“取消选择全部”,然后只选择“订阅”,然后点击“确定” -\n4. 点击“下一步”然后点击“创建导出” -\n5. 在“下载”按钮出现后,点击它 -\n6. 从下载的takeout压缩包提取.json文件 (通常能够位于\"YouTube and YouTube Music/subscriptions/subscriptions.json\")并在此导入它。 - 通过输入网址或你的 ID 导入 SoundCloud 配置文件: -\n -\n1. 在浏览器中启用\"电脑模式\"(该网站不适用于移动设备) -\n2. 转到此 URL: %1$s -\n3. 登录(如果需要) -\n4. 复制重定向的配置文件下载地址。 - 你的 ID:soundcloud.com/[你的ID] +\n1. 打开这个网页:%1$s; +\n2. 登录谷歌账号; +\n3.选择“YouTube 和 YouTube Music” ,然后点击“已包含所有YouTube数据”,然后取消全选,仅选择“订阅内容”,然后点击“确定” +\n4. 点击“下一步”,可以保持默认,然后点击“创建导出作业”; +\n5. 通过所指定的方式,下载takeout数据; +\n6. 从下载的takeout压缩包提取.json文件 (通常位于\"YouTube and YouTube Music/subscriptions/subscriptions.json\")然后在此导入。 + 通过输入网址或你的 ID 导入 SoundCloud 配置文件: +\n +\n1. 在浏览器中启用\"电脑模式\"(该网站未适配移动设备); +\n2. 打开该网站: %1$s; +\n3. 登录(如果需要); +\n4. 复制得到的配置文件下载地址。 + 你的 ID:soundcloud.com/[你的ID] 该操作消耗大量流量, +\n \n你想继续吗? - 关闭可防止加载缩略图,节已省数据和内存使用。(若现在更改会清除内存和储存中缓存) + 关闭可禁止加载封面,节省流量和内存使用。(现在更改该选项将清除内存与储存中全部缓存) 清空图像缓存成功 清空已缓存元数据 清空已缓存的网页数据 清空元数据缓存成功 - 播放速度 + 播放速度控制 节奏 音调 - 解除挂钩(可能导致失真) + 解除音视挂钩(可能导致失真) 首选“打开”操作 - 打开内容时默认操作: = %s - 无可下载的串流内容 + 打开内容时默认操作:- %s + 无可下载的串流 字幕 - 修改播放器字幕比例和背景样式。需要重新启动应用程序才能生效。 - 删除串流的播放历史和播放位置 - 删除全部观看历史记录? - 清除搜索历史记录 - 清除搜索关键词的历史记录 - 是否删除全部搜索历史记录? - 搜索历史记录已删除。 + 修改播放器字幕比例和背景样式,重启应用生效。 + 删除串流播放记录和播放位置记录 + 删除全部观看记录? + 清空搜索记录 + 清空搜索记录关键词 + 是否删除全部搜索记录? + 搜索记录已删除。 NewPipe 是版权自由软件:您可以随时使用、研究共享和改进它。您可以根据自由软件基金会发布的 GNU 通用公共许可证GPLv3或(由您选择的)任何更高版本的许可证重新分发或修改该许可证。 - 是否要同时导入设置? - NewPipe的隐私政策 + 是否要导入设置? + NewPipe 隐私政策 NewPipe 项目非常重视您的隐私。因此,未经您的同意,应用程序不会收集任何数据。 \nNewPipe 的隐私政策详细解释了在发送崩溃报告时发送和存储的数据。 阅读隐私政策 @@ -407,25 +410,25 @@ 接受 拒绝 无限制 - 使用移动数据时限制分辨率 + 使用移动数据播放时降低分辨率 退出应用时最小化 从主播放器切换到其他应用时的操作 - %s 静音时快进 - 滑块[比例尺] - 重 置 + 比例调整 + 重置 曲目 用户 选择标签 - 音量手势控制 + 手势控制音量 使用手势控制播放器的音量 - 亮度手势控制 + 手势控制亮度 使用手势控制播放器的亮度 视频默认语言 应用更新通知 NewPipe有新版本的通知 外置存储不可用 - 无法下载到外部 SD 卡。重置下载文件夹位置? - 读取已保存标签时发生错误,因此使用者默认标签 + 无法下载到外部 SD 卡,修改下载文件夹位置? + 读取已保存标签时发生错误,因此使用默认标签 恢复默认 是否恢复默认值? 选择 @@ -434,52 +437,52 @@ 自动 切换视图 点击下载 - 后期处理 + 处理中 生成唯一名称 覆盖 - 正在使用此名称进行下载 + 已存在一进行中并使用该名称的下载任务 显示错误 代码 无法创建目标文件夹 无法创建文件 权限被系统拒绝 - 安全连接失败 + 建立安全连接失败 找不到服务器 - 无法连接到服务器 + 无法连接至服务器 服务器未发送数据 找不到 NOT FOUND 后期处理失败 停止 最大重试次数 取消下载前的最多重试次数 - 在切换到移动流量网络时中断播放 - 切换至移动数据时可能有用,尽管一些下载无法被暂停 + 切换到移动流量网络后中断播放 + 切换至移动数据时可能有用,虽然部分下载无法被暂停 事件 - 近期大会 + 会议大会 显示评论 - 禁用,以停止显示评论 + 是否隐藏评论 无法加载评论 关闭 恢复播放 - 恢复上次播放位置 - 列表中的位置 - 在列表中,显示视频最后一次播放时的播放位置 + 自动定位到上次播放时位置 + 显示最后一次播放位置 + 在列表中,使用底端进度条显示某一视频上次播放时的播放位置 已删除播放位置记录。 - 文件被已移动或删除 + 文件已被移动或被删除 该名称的文件已经存在 - 命名冲突,已存在具有此名称文件 + 命名冲突,已存在具有该名称的文件 无法覆盖文件 有此名称的已暂停下载 NewPipe 在处理文件时被关闭 设备上没有剩余储存空间 进度丢失,文件已被删除 连接超时 - 是否要清除下载历史记录或删除所有下载的文件? - 最大下载队列 - 同时只允许一个下载进行 + 是否清空下载记录或删除所有下载的文件? + 限制下载并发数 + 同一时间内只允许进行一个下载任务 开始下载 暂停下载 - 询问下载位置 + 总是询问下载位置 系统将询问您将每次下载的保存位置 系统将询问您将每次下载的保存位置。 \n(如果要下载到外部 SD 卡,请选择 SAF) @@ -488,8 +491,8 @@ \n一些设备不兼容SAF 删除播放位置记录 删除所有播放位置记录 - 删除所有播放位置记录? - 更改下载目录让内容生效 + 是否删除全部播放位置记录? + 更改下载目录以生效 『时下流行』页-默认 没有人在观看 @@ -497,28 +500,28 @@ 没有人在听 - %s 人在听 + %s 位听众 - 重新启动应用后,语言将更改。 + 语言更改将在重启应用后生效。 PeerTube 服务器 - 设置自己喜欢的PeerTube服务器 - 查找最适合你的服务器%s + 设置自定义PeerTube服务器 + 查找你需要的服务器%s 添加服务器 输入服务器网址(URL) 无法验证服务器 - 仅支持 HTTPS和URL + 仅支持 HTTPS URL 该服务器已存在 本地 最近添加 - 最喜欢的 - 自动生成的(未找到上传者) + 最受欢迎 + 自动生成的(找不到上传者) 正在恢复 无法恢复此下载 选择一个服务器 - 快进 / 快退的单位时间 - 清除下载历史记录 - 删除已下载的文件 + 快进 / 快退的寻址定位时间间隔 + 清空下载记录 + 删除下载文件 已删除 %1$d 下载 授予在其他应用上层显示的权限 Newpipe应用语言 @@ -529,7 +532,7 @@ %d秒 - 由于ExoPlayer的限制,搜寻间隔设置为%d秒 + 由于ExoPlayer的限制,寻址间隔置为%d秒 静音 取消静音 帮助 @@ -548,37 +551,38 @@ 正在加载feed… 正在处理feed… 选择订阅 - 未选中订阅 + 未选中任何订阅 已选中%d - 组名为空 + 清空组名 您要删除该组吗? 新建 - 订阅 + Feed Feed更新阈值 - 上次更新后,订阅被视为过时的时间-%s + 上次更新后,订阅被视为过期的时间-%s 始终更新 - 可用时从专用feed获取 - 在某些服务中可用,通常速度要快得多,但可能返回的条目数量有限,而且信息通常不完整(例如,没有持续时间,条目类型,没有实时状态)。 + 可用时使用专用feed获取 + 仅在某些服务中可用,通常速度更快,但返回的视频数量可能有限,而且信息通常不完整(如,无视频持续时间、类型与没有直播状态)。 启用快速模式 禁用快速模式 - 您是否认为Feed加载太慢?如果是这样,请尝试启用快速加载(您可以在设置中更改它,也可以按下面的按钮更改它)。 + 您是否觉得Feed加载太慢?如果是这样,请尝试启用快速加载(可在设置中修改,也可使用下面的按钮修改)。 \n \nNewPipe提供两种feed加载策略: -\n•获取整个订阅频道,这很慢但是很是完整。 -\n•使用专用的服务端点,这样会比较快但通常不完整。 +\n•获取整个订阅频道,很慢但是很完整。 +\n•使用专用的服务端点,比较快但通常不完整。 \n -\n两者之间的区别在于,后者通常缺少一些信息,例如条目的持续时间或类型(无法区分直播视频和普通视频),并且可能返回更少的条目。 +\n两者之间的区别在于,后者通常缺少一些信息,如视频的持续时间或类型(无法区分直播视频和普通视频),并且可能返回更少的视频条目。 \n -\nYouTube是一个通过其RSS feed提供这种快速方法的服务示例。 +\nYouTube是一个通过其RSS feed提供此快速方法的服务示例。 \n -\n因此,选择哪种方式取决于您更看重什么:是速度还是精确的信息。 +\n因此,选择哪种方式取决于您的偏好: +\n加载速度还是信息准确。 NewPipe尚不支持该内容。 \n \n \n也许未来版本会支持它。 - ∞ 视频 + ∞ 部视频 100+部视频 艺术家 专辑 @@ -590,54 +594,57 @@ 由%s创建 频道的头像缩略图 是的,包括没看完的视频 - 已经看过且在之后被加入播放列表的视频将被移除。 -\n您确定吗?这不能被撤消! + 已经看过且在之后被加入播放列表的视频将被删除。 +\n您确定吗?操作不能被撤消! 移除看过的视频? 移除看过的视频 - 来自服务的原始文本将在流项目中可见 - 在项目上显示原始时间 + 来自服务的原始文本将在串流项目中可见 + 显示原始时间 打开YouTube\"受限模式\" 仅显示未分组订阅 播放列表页 - 尚无播放列表书签 + 尚无收藏 选择播放列表 请检查您的问题是否已经存在。创建重复票证时,您需要从我们那里花些时间来让我们修复真正的bug。 在GitHub上反馈 - 复制格式报告 + 复制已整理的报告 显示结果为:%s - 永不 + 从不 仅在Wi-Fi下 - 自动开始回放 — %s - 播放队列 - 无法识别该url。用另一个应用程序打开它\? - 自动排队 - 来自活跃播放器的队列将被替换 - 从一个播放器切换到另一个播放器后,你的队列可能会被替换 - 清除队列之前请求确认 + 视频开始播放后,自动定位到上次播放时的位置 — %s + 播放列表 + 无法识别该URL,用其他应用打开\? + 自动加入播放列表 + 活跃播放列表将被替换 + 从一个播放器切换到另一个播放器后,你的播放列表可能会被替换 + 清空播放列表前再次确认 缓冲 随机播放 重复 - 您最多可以选择三个操作显示在紧凑通知中! - 点击来编辑下面的每个通知动作。通过使用右边的复选框,选择其中最多三个显示在紧凑通知中。 + 您最多可以选择显示在紧凑通知中的三个操作选项! + 点击编辑下面的每个通知动作。使用右方的复选框,选择显示在紧凑通知中的动作,最多三个。 第五操作按钮 第四操作按钮 第三操作按钮 第二操作按钮 - 首选操作按钮 - 将通知中显示的视频缩略图长宽比从16:9缩放到1:1(可能会导致失真) - 缩放缩略图到1:1的长宽比 + 第一操作按钮 + 将通知中视频缩略图长宽比从16:9强制缩放到1:1(可能导致失真) + 强制缩放缩略图至1:1比例 通知 显示内存泄漏 已加入队列 加入队列 - 清理你在解决验证码时 NewPipe 存储的cookies - reCAPTCHA cookies 已被清理 - 清理 reCAPTCHA cookies - YouTube提供了一个“受限模式”,会隐藏潜在的成人内容 + 清空本地存储的reCAPTCHA验证码相关cookies + reCAPTCHA cookies 已被清空 + 清空 reCAPTCHA cookies + YouTube提供了“受限模式”,将隐藏潜在的成人内容 展示可能不适合儿童观看的内容,因为有年龄限制(比如18岁以上) - 让安卓系统根据视频缩略图的主色彩自定义通知的颜色(注意,该特性并非在所有设备上都可用) - 对通知着色 - 在锁定屏幕上显示缩略图为背景和内部通知 + 让Android系统根据视频缩略图的主色彩自主决定通知颜色(注意,该特性仅在部分设备上可用) + 自动着色通知 + 锁屏背景和通知中使用缩略图 显示缩略图 + 视频哈希值计算通知 + 视频正在哈希值计算时显示的通知 + 计算哈希值中 \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 812a1a815da..6a8f4c212ed 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -1,6 +1,7 @@ - Націсніце \"Пошук\", каб пачаць + Націсніце \"Пошук\", каб пачаць +\n %1$s праглядаў Апублікавана %1$s Патокавы плэер не знойдзены. Усталяваць VLC? @@ -36,11 +37,11 @@ У акне Дадаць да Каталог для спампаванага відэа - Папка для захоўвання загружаных відэа - Увядзіце шлях да папкі для загрузкі відэа + Папка для спампаванга відэа + Увядзіце шлях да папкі для спампавання відэа Тэчка загрузкі аўдыё - Сюды захоўваецца загружанае аўдыё - Увядзіце шлях да папкі для загрузкі аўдыё + Папка для спампаванага аўдыя + Увядзіце шлях да папкі для спампавання аўдыя Аўтапрайграванне Прайграваць відэа пры выкліку NewPipe з іншага прыкладання Разрознянне па змаўчанні @@ -61,7 +62,7 @@ Аднавіць акно Запамінаць памер і становішча ўсплываючага акна Хуткі пошук пазіцыі - Недакладны пошук дазваляе плэеру шукаць пазіцыю хутчэй, але менш дакладна + Недакладны пошук дазваляе плэеру шукаць пазіцыю хутчэй, але менш дакладна. Не працуе для перамоткі на 5, 15 ці 25 секунд Загружаць мініяцюры Адключыце, каб не загружаць мініяцюры і зэканоміць трафік і памяць. Змена налады ачысьціць кэш малюнкаў Кэш малюнкаў ачышчаны @@ -556,4 +557,7 @@ Паўтор Кнопка пятага дзеяння Паведамленні + Афарбоўваць апавяшчэнне асноўным колерам мініяцюры. Падтрымваецца не ўсімі прыладамі + У кампактным апавяшчэнні дасяжна не больш за тры дзеянні! + Дзеянні можна змяніць, націснуўшы на іх. Адзначце не больш за трох для адлюстравання ў кампактным апавяшчэнні \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 73ec90c82ae..7a8611d1cb6 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,6 +1,7 @@ - Докоснете търсачката, за да започнете + Докоснете търсачката, за да започнете +\n %1$s гледания Публикувано на %1$s Не е намерен стрийм плейър. Желаете ли да инсталирате VLC? @@ -12,12 +13,12 @@ Изтегли Търси Настройки - Може би имахте в предвид: %1$s\? + Може би имахте предвид „%1$s“\? Сподели с Избери браузър ориентация Използвай външен видео плейър - Премахва аудио при НЯКОИ резолюции + Премахва аудиото при някои резолюции Използвай външен аудио плейър NewPipe в прозорец Абониране @@ -34,13 +35,13 @@ Въведете път за съхранение на изтеглените видеота Директория за изтегляне на аудио Папка за съхранение на изтеглено аудио - Въведете път за съхранение на изтеглено аудио + Въведете папка за изтегляния на аудио файлове Автоматично възпроизвеждане Въпроизвежда видео, когато NewPipe е повикан от друго приложение Резолюция по подразбиране Резолюция по подразбиране на прозореца Покажи по-високи резолюции - Само някои устройства поддържат възпроизвеждане на 2K/4K клипове + Само някои устройства могат да възпроизвеждат 2K/4K видео Въпроизвеждане с Kodi Приложението „Kore“ не е намерено. Желаете ли да го инсталирате? Покажи „Възпроизвеждане с Kodi“ @@ -408,4 +409,16 @@ Продължи възпроизвеждане Изтрии данни Показване на резултати за: %s + Няма коментари + + %s слушател + %s слушатели + + Няма слушатели + + %s зрител + %s зрители + + Няма зрители + Изтрива всички позиции на възпроизвеждане \ No newline at end of file diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 3fde293d8f0..a33036846cd 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -437,7 +437,7 @@ ফাইল আমদানি করো রপ্তানি করো আমদানি করো - বুকমার্ক প্লেলিস্ট + প্লেলিস্ট বুকমার্ক করো তথ্য আনা হচ্ছে… পপআপ প্লেয়ার পটভূমি প্লেয়ার @@ -476,4 +476,17 @@ সিস্টেম ডিফল্ট সাফ ব্যবহার করো বিজ্ঞপ্তি রঙিন করো + + %d দিন + %d দিন + + + %d ঘন্টা + %d ঘন্টা + + + %d মিনিট + %d মিনিট + + সর্বোচ্চ চেয্টা \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index fbfe8eedad7..05e4d955cd9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -657,6 +657,6 @@ Zobrazit obsah, i když je patrně nevhodný pro děti, protože odkazuje na věkové omezení (např. 18+) Nechte Android přizpůsobit barvu oznámení podle hlavní barvy v miniatuře (není k dispozici na všech zařízeních) Barevné notifikace - Zobrazit miniaturu na zamknuté obrazovce jako pozadí a v oznámeních + Použít miniaturu pro pozadí zamknuté obrazovky a oznámení Zobrazit miniaturu \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6effa7615d2..2ec4579f697 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -105,7 +105,7 @@ Nicht unterstützter Server Konnte Bild nicht laden App/UI abgestürzt - Threads + Themen NewPipe lädt herunter Für Details antippen Ungültige URL oder Internet nicht verfügbar @@ -317,7 +317,7 @@ \nMöchtest du fortfahren\? Vorschaubilder laden Bilder-Cache gelöscht - Zwischengespeicherte Metadaten löschen + Zwischengespeicherte (Metadaten) löschen Alle zwischengespeicherten Website-Daten entfernen Metadatencache gelöscht Debug @@ -636,7 +636,7 @@ Automatische Warteschlange Den Player zu wechseln könnte deine Warteschlange überschreiben Bestätige das Leeren der Warteschlange - Die aktive Wiedergabeliste wird ersetzt werden + Die aktive Player-Warteschlange wird ersetzt Eingereiht YouTube bietet einen „Eingeschränkten Modus“, der potenzielle Inhalte für Erwachsene ausblendet Speicherlecks anzeigen @@ -644,9 +644,12 @@ reCAPTCHA-Cookies wurden gelöscht reCAPTCHA-Cookies löschen Zeige altersbeschränkte Inhalte (bspw. 18+), welche möglicherweise unpassend für Kinder sein könnten - Wiedergabe einreihen + In Wiedergabe einreihen Android kann die Farbe der Benachrichtigung entsprechend der Hauptfarbe in der Miniaturansicht anpassen (beachte, dass dies nicht auf allen Geräten verfügbar ist) Benachrichtigung farblich anpassen - Vorschaubild auf dem Sperrbildschirm als Hintergrund und innerhalb von Benachrichtigungen anzeigen + Vorschaubild für Sperrbildschirmhintergrund und Benachrichtigungen verwenden Vorschaubild anzeigen + Hash wird berechnet + Benachrichtigungen für den Video-hashing Fortschritt + Video Hash Benachrichtigung \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 5410adf5521..c7b189318df 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -108,7 +108,7 @@ Προεπιλεγμένη ανάλυση αναδυόμενου παραθύρου Εμφάνιση υψηλότερων αναλύσεων Προεπιλεγμένη μορφή βίντεο - Ενθύμιση τις ιδιότητες του αναδυόμενου παραθύρου + Ενθύμιση ιδιοτήτωναναδυόμενου παραθύρου Ενθύμιση του τελευταίου μεγέθους και θέσης του παραθύρου Χρήση γρήγορης ανακριβούς αναζήτησης Η μην ακριβής αναζήτηση επιτρέπει στην εφαρμογή να αναζητεί θέσεις στο βίντεο γρηγορότερα με μειωμένη ακρίβεια. Δε λειτουργεί για διαστήματα των 5, 15 ή 25 δευτερολέπτων. @@ -168,8 +168,8 @@ Αλλαγή σε Κύριο Εισαγωγή βάσης δεδομένων Εξαγωγή βάσης δεδομένων - Παρακάμπτει το τρέχον ιστορικό και τις εγγραφές σας - Εξαγωγή ιστορικού, εγγραφών και λιστών αναπαραγωγής + Παρακάμπτει το τρέχον ιστορικό, εγγραφές, λίστες αναπαραγωγής και (προαιρετικά) σας + Εξαγωγή ιστορικού, εγγραφών, λιστών αναπαραγωγής και Εκκαθάριση ιστορικού προβολής Διαγράφει το ιστορικό των αναπαραχθέντων ροών και των θέσεων αναπαραγωγής Διαγραφή ολόκληρου του ιστορικού προβολής; @@ -592,7 +592,7 @@ %d επιλέχθηκαν Δεν φορτώθηκε: %d - Οι ομάδες του καναλιού + Ομάδες καναλιών %d ημέρα %d ημέρες @@ -645,6 +645,9 @@ PeerTube instances Χρωματισμός ειδοποιήσεων Επιτρέπει στο Android να τροποποιήσει το χρώμα της ειδοποίησης, σύμφωνα με το κύριο χρώμα του εικονιδίου (δεν διατίθεται σε όλες τις συσκευές) - Εμφάνιση των εικονιδίων στην οθόνη κλειδώματος, ως φόντο και στις ειδοποιήσεις + Χρήση των εικονιδίων στην οθόνη κλειδώματοςως φόντο και στις ειδοποιήσεις Εμφάνιση + Υπολογισμός hash + Ειδοποιήσεις για πρόοδο βίντεο hashing + Ειδοποίηση βίντεο hash \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fe16a89bc70..2cf9f3b01b2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -650,6 +650,6 @@ YouTube provee un «Modo restringido», el cual oculta contenido potencialmente sólo apto para adultos Ajustar color de notificación Permitir a Android personalizar el color de la notificación con el color principal de la imagen (ten en cuenta que esta opción no funciona en todos los dispositivos) - Mostrar miniatura como fondo de pantalla de bloqueo y dentro de notificaciones + Usar miniatura como fondo de pantalla de bloqueo y notificaciones Mostrar vista previa \ No newline at end of file diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index a477cb27c4f..0dea4517832 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -278,8 +278,8 @@ Fitxategia Inportatu datu-basea Esportatu datu-basea - Zure uneko historiala eta harpidetzak gainidazten ditu - Esportatu historiala, harpidetzak eta erreprodukzio-zerrendak + Zure uneko historiala, harpidetzak eta (aukeran) ezarpenak gainidazten ditu + Esportatu historiala, harpidetzak, erreprodukzio-zerrendak eta ezarpenak Garbitu ikusitakoaren historiala Jotako jarioen historiala eta erreprodukzio puntuak ezabatzen ditu Ezabatu ikusitakoaren historia osoa\? @@ -647,6 +647,9 @@ Editatu beheko jakinarazpen ekintza bakoitza gainean sakatuz. Hautatu horietako hiru gehienez jakinarazpen trinkoan erakusteko eskuineko kontrol laukiak erabiliz. Androidek miniaturako kolore nagusiaren arabera jakinarazpenaren kolorea pertsonalizatzea baimendu (kontuan izan ez dagoela gailu guztietan erabilgarri) Koloreztatu jakinarazpena - Erakutsi miniatura blokeo pantailan atzeko planoko eta barruko jakinarazpen bezala + Erabili miniatura blokeo pantaila eta jakinarazpenentzako Erakutsi miniatura + Jakinarazpenak bideoen hashing egoerarako + Bideo hash jakinarazpena + Hash-a kalkulatzen \ No newline at end of file diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index c9026aded20..baa0e808481 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -156,7 +156,7 @@ Tauota Toista Poista - Tarkistus-suma + Tarkistussumma Uusi tehtävä OK Tiedostonimi @@ -272,8 +272,8 @@ Vaihda normaalitoistoon Tuo tietokanta Vie tietokanta - Kirjoittaa yli tämänhetkisen historian ja tilaukset - Vie historia, tilaukset tai soittolistat + Ylikirjoittaa tämänhetkisen historian, tilaukset, soittolistat ja (vaihtoehtoisesti) asetukset + Vie historia, tilaukset, soittolistat ja asetukset Poista katseluhistoria Poistaa toistohistorian ja toistokohdat Poista koko katseluhistoria\? @@ -647,4 +647,7 @@ Näytä mahdollisesti lapsille sopimaton sisältö, jolla on ikäraja (esim. 18+) Anna Androidin muokata ilmoituksen väriä esikatselukuvan päävärin mukaan (tämä ei ole mahdollista kaikilla laitteilla) Käytä värejä ilmoituksessa + Käytä esikatselukuvaa lukitusruudun ja ilmoitusten taustakuvana + Näytä esikatselukuva + Lasketaan \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4b04865bd5a..c128c42149e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -650,5 +650,5 @@ Notification colorée Demander à Android de personnaliser la couleur de la notification en fonction de la couleur principale de la miniature (noter que cela n’est pas disponible sur tous les appareils) Afficher la miniature - Afficher la miniature en arrière-plan de l’écran de verrouillage et dans les notifications + Utiliser la miniature pour l\'arrière-plan de l’écran de verrouillage et les notifications \ No newline at end of file diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 8da1918d315..e366a673cc0 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -667,6 +667,9 @@ הצגת תוכן שעלול להיות בלתי הולם לילדים עקב מגבלת גיל (כגון 18+) לאפשר ל־Android להתאים את צבע ההתראה בהתאם לצבע העיקרי של התמונה הממוזערת (לא זמין בכל המכשירים) צביעת ההתראה - הצגת תמונה ממוזערת על מסך הנעילה כרקע ובתוך ההתראות + להשתמש בתמונה ממוזערת גם כרקע מסך הנעילה וגם בהתראות הצגת תמונה ממוזערת + הגיבוב + התראות על תהליכי גיבוב + התראת גיבוב סרטון \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 063686d8f74..0146b5fff80 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -148,7 +148,7 @@ Dodirni za detalje Molimo pričekajte… Kopirano u međuspremnik - Kasnije odredite mapu za preuzimanje u postavkama + Molimo kasnije u postavkama odaberite mapu za preuzimanje Ova dozvola je potrebna za \notvaranje skočnog prozora reCAPTCHA zadatak @@ -335,11 +335,14 @@ Prethodni izvozi Nije moguće uvesti pretplatnike Nije moguće izvesti pretplatnike - Uvezite YouTube pretplatnike preuzimanjem izvozne datoteke: -\n -\n1. Idite na ovaj URL: %1$s -\n2. Ulogirajte se -\n3. Preuzimanje bi trebalo početi (to je izvozna datoteka) + Uvezite YouTube pretplatnike preuzimanjem izvozne datoteke Google-a: +\n +\n1. Idite na ovaj URL: %1$s +\n2. Prijavite se +\n3. Kliknite \"Uključeni svi podaci\", zatim \"Poništi odabir svih\", a zatim odaberite samo \"pretplate\" i kliknite \"U redu\" +\n4. Kliknite na \"Nastavi\", a zatim \"Stvori izvoz\" +\n5. Kliknite na \"Preuzmi\" +\n6. Preuzmite zip datoteku i izvucite json datoteku (pod \"YouTube and YouTube Music/subscriptions/subscriptions.json\") pa uvezite je ovdje vašID, soundcloud.com/vašID Uzmite u obzir da ova operacija može uzrokovat veliku potrošnju prometa. \n @@ -487,7 +490,7 @@ Podržani su samo HTTP URL-ovi Lokalno Nedavno dodano - Automatski generirana (nije pronađen nijedan autor) + Autogenerirano (prenositelj nedefiniran) Očisti povijest preuzimanja Izbriši preuzete datoteke Obrisano %1$d preuzimanja @@ -552,7 +555,7 @@ Promijenite mape za preuzimanje kako bi stupile na snagu Prikazuju se rezultati za: %s Nije moguće prepoznati URL. Želite li otvoriti u drugoj aplikaciji\? - Promijeni omjer minijature na 1:1 + Smanjiti omjer minijatura na 1:1 Učitavanje u predmemoriju Istovremeno se pokreće jedno preuzimanje Dodano u popis izvođenja @@ -645,4 +648,18 @@ \nNeki uređaji nisu kompatibilni Izvorni tekstovi usluga bit će vidljivi u elementima prijenosa Dostupno je u nekim uslugama. Obično je puno brže, ali može vratiti ograničenu količinu predmeta i često nepotpune podatke (npr. bez trajanja, vrste predmeta, bez stanja uživo). + Mislite li da je učitavanje feeda prespor\? Ako je to slučaj, pokušajte omogućiti brzo učitavanje (možete ga promijeniti u postavkama ili pritiskom na donji gumb). +\n +\nNewPipe nudi dvije strategije ulaganja feeda: +\n• Dohvaćanje cijelog pretplatničkog kanala, koji je spor, ali cjelovit. +\n• Korištenje namjenske krajnje točke usluge, koja je brza, ali obično nije potpuna. +\n +\nRazlika je u tome što brzom obično nedostaju neke informacije, poput trajanja ili vrste stavke (ne može razlikovati videozapise uživo od uobičajenih), a možda će vratiti i manje predmeta. +\n +\nYouTube je primjer usluge koja nudi ovaj brzi način sa svojim RSS feedom. +\n +\nDakle, izbor se svodi na ono što više volite: brzinu ili precizne informacije. + Izračunavanje šifriranja + Obavijest šifriranja videa + Obavijesti o napretku šifriranja videa \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 4e23cce03a0..b76274305c9 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -637,6 +637,7 @@ Menampilkan konten yang mungkin tidak cocok untuk anak-anak karena memiliki batasan umur (seperti 18+) Minta Android menyesuaikan warna notifikasi sesuai dengan warna utama di thumbnail (perhatikan bahwa ini tidak tersedia di semua perangkat) Warnai notifikasi - Tampilkan thumbnail pada layar penguncian sebagai latar dan di dalam notifikasi + Gunakan thumbnail untuk latar layar penguncian dan notifikasi Tampilkan thumbnail + Mengkalkulasi hash \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 9a3f6ef4ec7..86d125b70b0 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -162,9 +162,9 @@ Impossibile aggiornare l\'iscrizione Iscrizioni Novità - Cronologia ricerche + Cronologia delle ricerche Salva le ricerche localmente - Cronologia visualizzazioni + Cronologia delle visualizzazioni Salva la cronologia degli elementi visualizzati Riprendi la riproduzione Continua a riprodurre dopo le interruzioni (es. telefonate) @@ -350,7 +350,7 @@ Carica copertine Disabilita per prevenire il caricamento delle copertine, risparmiando dati e memoria. La modifica di questa opzione cancellerà la cache delle immagini in memoria e sul disco. Cache immagini svuotata - Pulisci Cache Metadati + Svuota la cache dei metadati Elimina i dati delle pagine web memorizzati nella cache Cache metadati svuotata Controlli della velocità di riproduzione @@ -363,14 +363,14 @@ Sottotitoli Modifica dimensione e stile dei sottotitoli. Riavviare per applicare le modifiche. Nessuna app installata per riprodurre questo file - Pulisci cronologia visualizzazioni + Elimina la cronologia delle visualizzazioni Elimina la cronologia degli elementi riprodotti e le posizioni di riproduzione Eliminare la cronologia delle visualizzazioni\? - Cronologia visualizzazioni eliminata. - Pulisci cronologia ricerche + Cronologia delle visualizzazioni eliminata. + Elimina la cronologia delle ricerche Elimina la cronologia dei termini di ricerca Eliminare la cronologia delle ricerche\? - Cronologia ricerche eliminata. + Cronologia delle ricerche eliminata. 1 elemento eliminato. NewPipe è un software libero con licenza copyleft: si può utilizzare, studiare, condividere e migliorare a proprio piacimento. In particolare, è possibile ridistribuirlo e/o modificarlo secondo i termini della GNU General Public License (Free Software Foundation), nella versione 3 o successiva, a propria discrezione. Vuoi anche importare le impostazioni? @@ -399,7 +399,7 @@ Disiscriviti Nuova scheda Scegli scheda - Gesti Controllo Volume + Gesti controllo volume Utilizza i gesti per controllare il volume del lettore multimediale Gesti controllo luminosità Utilizza i gesti per controllare la luminosità del lettore multimediale @@ -468,7 +468,7 @@ Recupera l\'ultima posizione di riproduzione Posizioni nelle liste Mostra gli indicatori della posizione di riproduzione nelle liste - Pulisci dati + Elimina dati Posizione di riproduzione eliminata. File spostato o cancellato Esiste già un file con questo nome @@ -477,7 +477,7 @@ NewPipe è stato chiuso mentre lavorava sul file Spazio insufficiente sul dispositivo Progresso perso poiché il file è stato eliminato - Pulire la cronologia dei download o eliminare tutti i file scaricati\? + Cancellare la cronologia dei download o eliminare tutti i file scaricati\? Sarà avviato un solo dowload per volta Avvia i download Sospendi i download @@ -489,7 +489,7 @@ \nScegli SAF se vuoi scaricare su una scheda SD esterna Lo Storage Access Framework consente di salvare file su una memoria esterna. \nAlcuni dispositivi non sono compatibili - Elimina posizioni di riproduzione + Elimina le posizioni di riproduzione Elimina tutte le posizioni di riproduzione Eliminare tutte le posizioni di riproduzione\? Modifica le cartelle di download per renderle effettive @@ -522,7 +522,7 @@ recupero Impossibile recuperare questo download Scegli un\'istanza - Pulisci cronologia download + Elimina la cronologia dei download Elimina file scaricati %1$d download eliminati Consentire la visualizzazione sopra altre applicazioni @@ -600,7 +600,7 @@ \nSei sicuro\? L\'azione è irreversibile! Rimuovere i video già visti\? Rimuovi elementi visti - Attiva la «Modalità con restrizioni» di YouTube + Attiva la \"Modalità con restrizioni\" di YouTube I testi originali dei servizi saranno visibili negli elementi video Mostra i tempi originali degli elementi Immagine del canale @@ -641,12 +641,15 @@ Aggiunto alla coda Accoda Cancella i cookie che NewPipe memorizza quando si risolve un reCAPTCHA - Cookie reCAPTCHA puliti - Pulisci cookie reCAPTCHA - Consente di usufruire della «Modalità con restrizioni» di YouTube, che esclude contenuti potenzialmente inappropriati per i minori + Cookie reCAPTCHA eliminati + Elimina cookie reCAPTCHA + Consente di usufruire della \"Modalità con restrizioni\" di YouTube, che esclude contenuti potenzialmente inappropriati per i minori Mostra contenuti che hanno un limite di età (es. 18+). Potrebbero essere inadatti ai bambini Lascia che Android modifichi il colore della notifica, secondo il colore principale della copertina (funzione non disponibile per tutti i dispositivi) Colora notifica - Mostra le copertine come sfondo della schermata di blocco e all\'interno delle notifiche + Utilizza le copertine come sfondo della schermata di blocco e per le notifiche Mostra copertina + Calcolo dell\'hash + Notifica Hash Video + Notifiche per lo stato di avanzamento dell\'hashing video \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e9253598d13..34df2592e91 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -495,7 +495,7 @@ 誰も聴いていません - %s リスナー + %s が聴取中 アプリを再起動すると、言語が変更されます。 高速早送り/巻き戻し時間 diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index a9ed083794c..36ed9d6654d 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -649,4 +649,7 @@ Fargelegg merknad Vis miniatyrbilde på låseskjerm som bakgrunn og i merknader Vis miniatyrbilde + Regner ut sjekksum + Merknad for videosjekksummeringsframdrift + Videosjekksumsmerknad \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 0217368da30..e120750fc75 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -264,8 +264,8 @@ Bezig met laden van gevraagde inhoud Databank importeren Databank exporteren - Dit overschrijft je huidige geschiedenis en abonnementen - Exporteer geschiedenis, abonnementen en afspeellijsten + Dit overschrijft je huidige geschiedenis, abonnementen, afspeellijsten en (optionele) settings + Exporteer geschiedenis, abonnementen, afspeellijsten en settings Geëxporteerd Geïmporteerd Geen geldig ZIP-bestand @@ -647,4 +647,6 @@ Toon inhoud die mogelijk niet geschikt is voor kinderen omwille van een leeftijdslimiet (zoals 18+) Laat Android de kleur van de notificatie aanpassen, op basis van de meest voorkomende kleur in de thumbnail (let op: niet beschikbaar op elk apparaat) Notificatie kleur aanpassen + Toon miniatuurafbeelding op het vergrendelscherm als achtergrond en binnen meldingen + Toon miniatuurafbeelding \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d4ee389f2f8..231702c24a5 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -657,6 +657,6 @@ Pokaż treści nieodpowiednie dla dzieci, ponieważ mają ograniczenia wiekowe (np. 18+) Niech Android dostosuje kolor powiadomienia zgodnie z głównym kolorem na miniaturze (nie jest to dostępne na wszystkich urządzeniach) Pokolorowanie powiadomienia - Pokazuj miniaturkę na ekranie blokady jako tło oraz wewnątrz powiadomień - Pokaż miniaturkę + Używaj miniatury zarówno jako tła ekranu blokady, jak i powiadomień + Pokazuj miniaturę \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c0e61b3929b..b9c352e7957 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -647,6 +647,9 @@ Mostrar conteúdo possivelmente inadequado para crianças porque tem um limite de idade (como +18) Permite o Android personalizar a cor da notificação de acordo com a cor principal da miniatura (note que isso não está disponível em todos os dispositivos) Colorir notificação - Mostrar miniaturas como fundo da tela de bloqueio e nas notificações + Usar miniatura para o plano de fundo da tela de bloqueio e as notificações Mostrar miniatura + Calculando hash + Notificações para o progresso do hash do vídeo + Notificação de Hash do Vídeo \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 9c40002306a..4799c6ea004 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -647,6 +647,9 @@ Mostrar conteúdo possivelmente impróprio para crianças porque tem um limite de idade (como 18+) Fazer com que o Android personalize a cor da notificação de acordo com a cor principal na miniatura (esta opção não está disponível em todos os dispositivos) Colorir notificação - Mostrar miniaturas no ecrã de bloqueio como fundo e como notificação + Usar miniaturas no fundo do ecrã de bloqueio e em notificações Mostrar miniatura + A calcular \'hash\' + Notificar sobre o progresso das \'hash\' de vídeos + Notificação \'hash\' do vídeo \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c672c648f8d..433bd50fb99 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -83,7 +83,7 @@ Трансляции пока не поддерживаются Не удалось загрузить изображение Приложение/UI завершило работу - Простите, это не должно было произойти. + Никогда такого не было, и вот опять. Отправить отчёт по e-mail Извините, что-то пошло не так. Отчёт @@ -592,7 +592,7 @@ \n \nВыбор за вами: скорость или точность. Обновление по RSS, если доступно - Доступно для некоторых сервисов, быстрое, но может возвращать не всё содержимое канала и не содержать часть сведений (длительность, статус трансляции) + Доступно для некоторых сервисов, быстрое, может возвращать не всё содержимое канала и не содержать часть сведений (длительность, статус трансляции) Период актуальности подписок после обновления — %s Это видео имеет возрастное ограничение. \n @@ -658,6 +658,9 @@ Удалить сохранённые при решении reCAPTCHA куки Окрашивать уведомление основным цветом миниатюры. Поддерживается не всеми устройствами Цветное уведомление - Отображать миниатюру в уведомлении и на экране блокировки + Использовать миниатюру для фона уведомлений и экрана блокировки Показать миниатюру + Показать уведомление при хэшировании видео + Уведомление о хэшировании + Вычисляется хэш \ No newline at end of file diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml index d04b2be1c5c..f4827ca772b 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -40,4 +40,5 @@ ᱯᱳᱯᱟᱹᱯ ᱵᱮᱠᱜᱨᱟᱩᱸᱰ ᱴᱮᱵᱽ ᱪᱚᱭᱚᱱ ᱢᱮᱸ + ᱰᱟᱩᱱᱞᱳᱰ ᱠᱟᱱ ᱣᱤᱰᱤᱭᱚ ᱨᱮᱫ ᱱᱚᱰᱮ ᱫᱚᱦᱚᱜᱼᱟ \ No newline at end of file diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 2127434b66b..8f9198d3f3c 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -647,6 +647,9 @@ Pedi a Android de personalizare su colore de sa notìfica sighende su colore printzipale de sa miniadura (ammenta·ti chi custu no est a disponimentu pro totu sos dispositivos) Colora sas notìficas Ammenta s\'ùrtima mannària e sa positzione in sa ventanedda - Ammustra sa miniatura in s\'ischermada de blocu e in intro de sas notìficas + Imprea sa miniatura siat comente isfundu de s\'ischermada de blocu e siat in sas notìficas Ammustra sa miniatura + Carculende s\'hash + Notìficas pro su protzessu de hashàgiu de su vìdeu + Notìfica de hash de su vìdeu \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index e08ab1b7ba6..9f1639644a3 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -658,5 +658,8 @@ Nechajte Android, aby prispôsobil farbu upozornenia podľa hlavnej farby v miniatúre (nemusí to fungovať na všetkých zariadeniach) Farby upozornení Zobrazovať miniatúru - Zobrazovať miniatúru pri uzamknutej obrazovke a v upozorneniach + Používať miniatúru ako pozadie pri uzamknutej obrazovke a v upozorneniach + Počítanie hash + Upozornenie pri generovaní hash z názu videa + Oznámenie o hashovaní videa \ No newline at end of file diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml new file mode 100644 index 00000000000..0ae0fc31ac6 --- /dev/null +++ b/app/src/main/res/values-so/strings.xml @@ -0,0 +1,496 @@ + + + Isticmaal dhaa-dhaafinta dagdaga ah (100% sax ma aha) + Xusuusnow meeshii iyo cabirkii u dambeeyay ee + Xusuusnow fadhiga daaqada + Madow + Nashqada + Nooca muuqaalka joogtada loo isticmaali doono + Nooca codka joogtada loo isticmaali doono + Android-ka haku badalo midabka ogaysiiska midabka galka waxa daaran (aaladahoo dhan looma wada heli karo nidaamkan) + idabbee ogaysiiska + Soo Kucinay + Isku + Ku + Ugu badnaan waxad dooran kartaa sadex wax iney ka muuqdaan ogaysiiska yar! + Wax ka badal ficilka ogaysiiska hoose oo walba adigoo dushiisa ku dhufanaya. Dooro ilaa sadex kamida si ay uga muuqdaan ogaysiiska yar adigoo taabanaya santuuqa dhanka midgta ku yaala. + batoonka ficilka koowaad + batoonka ficilka koowaad + batoonka ficilka sadexaad + batoonka ficilka + batoonka ficilka koowaad + La ekaysii galka muuqaalka xaga ogaysiisyada ka muuqda cabirka 1:1 adoo kasoo badalaya 16:9 (wuxuu keeni karaa shucaac) + Galka la ekaysii cabirka 1:1 + Soo bandhig istikhyaar ah in muuqaal lagu furo xarunta madadaalada + Soo bandhig istikhyaarka \"Kufur Kodi\" + Kushub app-ka maqan ee Kore\? + Kufur + Aaladaha qaar kaliya ayaa furi kara muuqaalada 2K/4K ga + Tus cabirada muuqaalka ee sare + Cabirka muuqaalka daaqada marwalba + Cabirka Muuqaalka ee + Wuxuu daaraa muuqaal marka NewPipe laga soo furo app + Badal meelaha waxa lasoo dajiyay galaan si uu u hirgalo waxaad badashay + Dooro meesha dhagaysiga lasoo dajiyay galaan + Dooro meesha muuqaalada lasoo dajiyay galaan + Dhagaysiga lasoo dajiyay halkan ayaa lagu kaydiyaa + Muuqaalada lasoo dajiyay halkan ayaa lagu kaydiyaa + Meesha oodajinta dhagaysiga + Meesha soodajintu + Ku Dar + Si + Xaga Dambe + Dooro Daaqada + Daaqad Cusub + Xulalka la + Rukunka + Guud + Tus xogta + Lama cusbooneysiin karo rukunka + Lama badali karo rukunka + Kanaalka waad iskajoojisay + Iskajooji Rukumashada + Rukuntay + Rukumo + Si + Isticmaal dhagaysi daare dibada + Codka ayuu ka saaraa cabirrada muuqaalkaqaar + Isticmaal muuqaal daare dibada + Dooro + La Wadaag + Soobandhigaya natiijada: %s + Ma waxaad ka waday \"%1$s\"\? + Fadhiga + Daji midka socda + La + Kufur daaqad yar + Kufur browser + Ka + Ku + Wax fura lama helin. (waxad Ku shuban kartaa VLC si aad u furto). + la furay %1$s jeer + Wax fura lama helin. Ku shubo VLC\? + Lasoo galiyay %1$s + ku dhufo \"Raadi\" si aad u bilawdo +\n + Shay magacan leh ayaa horay u + Ku + Usamee magac gaar + %s soodajin ayaa dhamaatay + Dajintii wuu dhamaatay + Dajintii ma guulaysto + Waxaa diiday + Kasoo + La + La + Hawsha Kujira + La + Ku dhufo si aad u dajiso + Nooc cusub oo NewPipe ah ayaa diyaar ah! + Baddal + Qaabka + Marna + Kaliya marka WiFi la isticmaal + Yaree marka app kale loo + Cusboonaysiinta + Xad + Dib + Horay u dhaafi meelaha + Maamulista Xawaaraha Waxa + yourID, soundcloud.com/yourid + Rukunka lama gudbin + Lama soo galin karo + Gudbintii + Soo gali + Gudbinaya… + Soo galinaya… + U + Kasoo + Soo + Soo gali/ + "Qoraaladii asal ahaan adeegyada la socday way ka muuqan doonaan waxyaabaha" + Soo bandhig wakhtigii asalka ahaa ee kasoo wareegay (shayyada) + Ku khasab warinta kareebitaanada aan la fulin karin ee Rx ka ee dibada qayb ama wakhtiga hawsha kadib marka la iska + Wari khaladaadka wakhtigoo + Tus ciladaha + Dabagalka cilada kaydka waxay sababi kartaa inuu app-ku istaago marka \'heap dump\' + Waxkabadal cabirka qoraalka qoral-hooseedka iyo midabka xaga dambe. App-ka waa in dib loo furaa si ay u hirgasho. + So + Ku + Qoral-Hoosaas majiro + Isasoo-bilaabay (Soo-galiye lama helin) + Xulka lama saari karo. + Galkii xulka waa la badalay. + La + Xulka waa la + Saar xulkan\? + Ka saar alaamadinta + Calaamadi + Ku Fadhiisi Galka Xulka + adalsii + Ku dar + Magaca kabadal + Tirtir + Xul + Sookicinaya shayga la + Helaya + Waydii + Gadaal ku + Muuqaal + Ficilka la doorbiday maeka shay la furayo — + Ficilka \'fur\' loo doorbiday + Wax baa halkan kasoo muuqan dhawaan ; + Xidh + Fur + Bilaw inaad daaqad ku + Ka bilow daarista + Bilaw inaad xaga dambe ka + La + Xaji si loo + Fadhiga + Faahfaahin + Ka + Daar + Inta loogu jecelyahay + Dhawaan lagu soo + Shiddan + Cusub oo + 50ka ugu + Bandhig + Luuqadu waxay isbadali doontaa marka barnaamijka dib loo soo kiciyo. + Faalooyinka lama soo dhigi + Inaad sidoo kale fadhiga soo galiso ma rabtaa\? + Tani waxay badali fadhigaaga hadda. + Digniin: Lama soowada galin karo shayyada oo + Shay ZIP ah oo sax ah + Lasoo + La + Dooro + Wax xul ah wali lama + Dooro + Wax kanaal ah wali lama + Dooro + Boga + Boga + Boga + Bandhiga siduu + Boga + Bog + Daaqadaha la soobandhigo boga + Boga guud waxa ka + Badanaa la + Kii udambeeyay ee la + Ma hubtaa inaad ka saari rabto shaygan kaydka taariikhda\? + Ma rabtaa inaad ka saarto shaygan kaydka wixii la daawaday\? + Ma rabtaa inaad ka saarto shaygan kaydka wixii la raadiyay\? + Waa la + Taariikhdii waa la nadiifiyay + Kaydka taariikhda wuu madhan + Taariikh + Kaydinta taariikhda way + La + La + Akhri + NewPipe waa barnaamij bilaash ah oon lahayn xuquuqda daabacaada: Waad isticmaali kartaa, wadaagi kartaa waadna hormarin kartaa hadaad rabto. Gaar ahaan waad sii daabici kartaa ama wax baad ka badali kartaa adigoo raacaya shuruudaha sharciga guud ee GNU sida ay soosaareen Ururka Barnaamijyada Bilaashka ah, soosaarista 3aad ee laysinka, ama (hadaad doonto) nooc walba oo kasii dambeeyay kii 3aad. + Siyaasada Sir-Dhawrka NewPipe + Laysanka NewPipe + Akhri siyaasada sir-dhawrka + Mashruuca NewPipe sir dhawrkaaga aad ayuu u daneeyaa. Sidaas darteed, App-ku wax xogtaada ah ma uruuriyo fasax la\'aan. +\nSiyaasada sir-dhawrka NewPipe ayaa si faahfaahsan u sharaxda wixii xog ah ee la diro lana kaydiyo markaad cilad farsamo wariso. + Booqo website-ka NewPipe xog intaas dheer iyo warar. + Website-ka + U fidi caawin + NewPipe waxaa sameeyay dad iskood isku xilqaamay oo wakhtiga ay xorta yihiin ku kharash gareeya inay kuu keenaan wax markaad isticmaalayso aad ku qanacdo. U fidi taageero sameeyaasha appka si ay NewPipe xataa sidan oga sii fiicneeyaan. + Ugu Deeq + Xaga GitHub fur + Hadaad hayso fikarodo; rogid, qaab badal, nafiinta \'code\', ama \'code-ka\' ood si wayn wax oga badashaa—caawinta marwalba waa lasoo dhawayn. Waxbadan hadii la qabto waxbadan ayaa fiicnaan! + Kusoo kordhin + Waa app fudud oo bilaash wax loogu daawado Android-ka. + Laysimada + Waxaa wax kusoo kordhiyay + Ku saabsan + Fur website-ka + Laysanka lama soo kicin karo + © %1$s sameeyay %2$s asagoo raacaya %3$s + Laysinada gacanta sadexaad + Xog + Fadhiga + Ku saabsan NewPipe + Barnaamij shaygan fura kuma jiro + Xarfaha gaarka ah kuwa ugu badan + Xarfaha iyo Godadka + Xarafka lagu badali + Xarfaha aan la taageerin waxaa lagu badali midkan + Xarafyada magaca shayga loo ogol yahay + Daji + Waan dhameeyay + Tijaabada reCAPTCHA ayaa la codsaday + Taabo \"Waan dhameeyay\" markaad xaliso + Tijaabada reCAPTCHA + 1 shay ayaa la saaray. + Ogolaanshahan ayaa loo baahan yahay si +\nloogu furo qaabka daaqada + Fadlan meesha wax lagu dajin doono hadhawto xaga fadhiga ka dooro + Dhakada ayaa lagu qabtay + Fadlan sug… + Xisaabinaya waxa isbadalay + Fahfahinta kusii dhufo + NewPipe Wuu Dajinayaa + Tixraac khaldan ama khad baan jirin + Shaygan horay ayuu ujiray + Martigaliye aan la taageerin + Khalad + Maqaallo + Magaca shayga + Hagaag + Hawl cusub + Magaca kabadal + Iska dhaaf + Xaqiijiyaha + Dhammaan tirtir + Hal Xabo Tirtir + Tirtir + Samee + Daar + Qabo + Bilaw + Faalooyin ma jiraan + + %s muuqaal + %s muuqaalo + + ∞ muuqaalo + 100+ muuqaal + Muuqaalo ma jiraan + + %s dhagayste + + + Cidna ma dhagaysanayso + + %s ayaa daawanaya + %s ayaa daawanaysa + + Cidna ma daawanayso + + %s ayaa furtay + %s ayaa furatay + + Lama furin + inta rukumatay lama heli karo + + %s ayaa rukuntay + %s ayaa rukumatay + + Dad rukuntay ma jiraan + Furo adeega, hada waxaa dooran: + B + K + M + App-ka u ogolow kaydka aaalada marka hore + Markale isku day + Cod + Muuqaal + Waa la sameeyay galka soodajinta ee \'%1$s\' + Lama samayn karo galka soodajinta ee \'%1$s\' + Jiid si aad ukala hormariso + Meel madhan + Natiijo lama helin + Isticmaal warka + Wari khalad + (Waa tijaabo) Ku khasab in soodajinta la dhexmariyo Tor si aad sirtaada u ilaashato (Muuqaalada tooska ah looma heli karo hadda). + Intaan ka helin + Isticmaal Tor + Inta ka heshay + Sawirka soosaarha muuqaalka + Daar muuqaalka, intuu socdo: + Waxa:\\nCodsi:\\nShayga Luuqada:\\nWadanka Shayga:\\nApp-ka Luuqada:\\nAdeega:\\nGMT Wakhtiga:\\Xidhmada:\\nNooca:\\nNooca barnaamijka: + Galka muuqaal tusaha + Faahfaahin: + Faaladaada (oo Ingiriis ah): + Waxa dhacay: + Xog: + Wari + Waan ka xunahay, waxbaa khaldamay. + Fadlan hubi in arin ciladdaada ka hadlaya horay loo wariyay. Marka wax horay u jiray la wariyo markale, wakhti ayaad naga qaadaysaa kaasoo aan cilada ku sixi la hayn. + Ku wari GitHub-ka + Koobiyee warka oo diyaarsan + Khaladkan email ahaan ku warceli + Wan ka xunahay, sidaa inay dhacdo ma ahayn. + U ogolow app-ka inuu dul fuulo applicationada kale + Ma rabtaa inaad sidii hore ku celiso\? + Dib ugu fadhiisi sidii hore + Lama akhrin karo daaqadihii la kaydiyay, ...isticmaalaya kuwii app-ku kusoo baxay + Wax la dajiyo lama heli karo + Khalad ayaa ka dhacay: + Magaca shayga ma madhnaan karo + Shaygani ma jiro ama ogolaansho looma haysto in wax laga badalo + Shaygan ma jiro/tixraacan + Galkan ma jiro + Shaygan waa la guuriyay ama waa la tirtiray + Codad la dhagaysto lama helin + Muuqaalo la daawado lama helin + Tixraac khaldan + Muuqaal daarayaasha dibada ah linkiyda noocan ah ma furaan + Kasoo kabashadii muuqaal daaraha khalad ayaa ka dhacay + Khalad aan laga soo kaban karin ayaa dhacay + Muuqaalkan lama daari karo + App-ka/UI-ga ayaa khalkhalay + Sawirka lama soo kicin karo + Wax la daawado lama heli karo + Muuqaalada tooska ah wali lama taageerin + Lama soo kicin karo meeshii soodajinta + Shaygan lama heli karo + Website-ka si buuxda looma furi karo + Lama furi karo website-ka + Lama badali karo sixiixa tixraaca muuqaalka + Lama kicin karo galalka + Khalad xaga khadka ah + Ku dajinta kaydka dibadda ee SD-ga suurtogal ma aha. Dib u fadhiisi meesha wax lagu dajiyo\? + Kaydka dibadda lama heli karo + Khalad + Caawin + Wixii la raadiyay waa la tirtiray. + Tirtir dhamaan wixii la raadiyay\? + Wuxuu tirtiraa kaydka wixii la raadiyay + Meelihii ay marayeen waa la tirtiray. + Tir + Tirtir dhamaan meelaha ay marayaan\? + Wuxuu tirtiraa meelihii ay kuu marayeen + Tirtir meelaha ay marayaan waxa la daaray + Shay + Dib-Ucabirida + Nadiifi + Fanaan + Albumo + Heeso + Qaybo + Xulal + Xul + Kanaalo + Kanaal + Soodajinta + Soodajinta + Toos + Ka noqo + Cusboonaysii + Kala shaandhee + Xidhan + Haa + Isticmaal + Muuqaal + Dhamaan + Kaydka wixii ladaawaday waa la tirtiray. + Tirtir gabi ahaan kaydka wixii ladaawaday\? + Wuxuu tirtiraa kaydka wixii la daawaday iyo meelihii ay kuu + Nadiifi kaydka wixii ladaawaday + Nadiifi kaydka uu NewPipe kaydiyo markaad xaliso reCAPTCHA + Dibadda u gudbi kaydka wixii la daawaday, rukunka, xulalka iyo + Wuxuu badalaa kaydka waxaad daawatay, rukunka, xulalka iyo (hadaad doonto) + Kaydkii reCAPTCHA waa la + Nadiifi kaydka reCAPTCHA + Gudbi + Soo gali xog kaydsan + U badal + U baddal + U baddal xaga dambe + Dhinaca + [Garanwaa] + Ogaysiiska adalida Muuqaalka + Ogaysiisyada heerka uu marayo badalida muuqaalka + Ogaysiisyada nooca cusub ee + Ogaysiiska Cusbonaysiinta NewPipe + Ogaysiisyada NewPipe markuu gadaal ka shidan yahay + Ogaysiisyada NewPipe + Hal + Mar + Daar + Waa la saaray + Tayada + Hadhow + Dhacdooyin + Faahfaahinta + Muuqaalkan da\'da ayuu ku xidhan yahay +\n +\nXaga fadhiga ka fur \"%1$s\" hadaad rabto inaad furto. + YouTube-ku wuxuu leeyahay \"Qaabka Xadidan\" kaasoo qariya waxyaabo laga yaabo ineysa haboonayn + Fur nidaamka YouTube-ka \"Qaabka Xadidan\" + Soo bandhig muuqaalada laga yaabo inaysa ku haboonayn caruurta sababtoo ah waxay ku xidhan yihiin da\'da (sida 18+) + Tus muuqaalada da\'da ubaahan + shay-ga + Waxaa lagu horay daaqada + Waxaa lagu horay xaga dambe + Ku daaraya daaqada + Ka daaraya xaga dambe + Ogaysiisyada + Cusboonaysiinta + Cilad-bixinta + Waxyaabo kale + Muuqaalka + Daaqada + Taariikhdka & kaydka kumeelgadhka + Muuqaalka iyo codka + Muuqaal daareha + Dabeecada + Qaybtan horay ayay ujirtay + Kaliya waxaa la furi karaa URL-lada HTTPS ka ah + Lama ansixin karo qaybtan + Gali URL-ka qaybta + Ku dar qaybta + %s ka hel qaybaha aad jeceshahay + Dooro qaybaha aad jeceshahay ee PeerTube-ka + Qaybaha PeerTube + Luuqada muuqaalada + Adeeg + Wadanka muuqaalada + Lama garan karo URL-kan. Ku fur app kale\? + URL aan la furi karin + Tus sharaxaada marka la riixayo batoonka gadaal ama midka daaqada ee ku yaala \"Faahfaahinta:\" muuqaalka + Tus sharaxaha \"Farta ku hay si aad iskugu darto\" + Soo dhig muuqaalada \"Ka xiga\" iyo \"Kuwa lamidka h\" + Isdaarida + Daji + Sii wad wixii daarada marka la dhabqiyo kadib (tus. wicitaanada) + Sii wad + La soco muuqaalada ladaawaday + Tirtir xogta + Kusoo bandhig meelaha laga daarayo liis-tada + Meelaha liis-tada ku jira + Kasii wad meeshii hore + Siiwad wixii hore + Wixii horay looraadiyay + Kaydi wixii la raadiyay (aalada) + Wixii laraadiyay + Tus soojeedino marka wax la raadinayo + Soojeedinta Raadinta + Isticmaal fartaada si aad umaamusho iftiinka iyo codka + Maamulista daaraha (farta) + Isticmaal fartaada si aad u maamusho iftiinka + Maamulista iftiinka (farta) + Isticmaal fartaada si aad umaamusho codka + Maamulista codka ee farta + Hormada-isutalisa + Sii wad dhamaynta (mida aan isku celcelinin) hormada shidan ayadoo lagu darayo waxyaabo la xidhiidha + Ku xiji hormada la daari doono + Waa la tirtiray yaryarkii + Tirtir waxyaabaha kumeelgaadhka ah + Tirtir waxyaabaha yaryar + kaydkii kumeelgaadhka ahaa ee sawirka waa la tirtiray + Xidh si aad u joojiso soobandhiga galalka muuqaalada, adigoo yaraynaya isticmalka khadka iyo maskaxda. Waxkabadalkan wuxuu nafiifin waxa kaydka hore iyo ka caadiga ah kumeelgaadh ahan ugu jira. + Xidh si aad u qariso aragtiyada + Tus aragtitada + Soosaar galka muuqaalka + Hormada muuqaal daaraha hada wax shidaya waa la badali doonaa + Kalabadalka muuqaal daaraha waxay badali kartaa hormada + Waydii in la xaqiijiyo intan hormada la tirtirin + Horay-udhaafinta/-dibucelinta wakhtigeeda + Dhaaf-dhaafinta waxa daaran ee aan boqolkiiba boqol saxda ahayn waxay u sahashaa muuqaal daaraha inuu u dhaaf dhaafiyo si dagdag ah. Nidaamkan 5, 15 ama 25 ilbiriqsi wax looma dhaafdhaafin karo. + Madow + Caddaan + Cod + Waxba + Isdaar + gaddinta + Raadi + Daji + \ No newline at end of file diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index a65e2639435..243f77f1109 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -2,7 +2,7 @@ %1$s приказа Објављен %1$s - Нема плејера токова. Инсталирати ВЛЦ\? + Нема плејера токова. Желите ли да инсталирате ВЛЦ\? Инсталирај Одустани Отвори у прегледачу diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index bc2b4ba5d63..d105be597f1 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -645,8 +645,11 @@ reCAPTCHA çerezlerini temizle YouTube, olası yetişkin içeriği gizleyen \"Kısıtlı Kip\" sağlamaktadır Yaş kısıtı (18+ gibi) nedeniyle çocuklara uygun olmayabilecek içeriği göster - Küçük resmi kilit ekranında arka plan olarak ve bildirimlerin içinde göster + Hem kilit ekranı arka planı hem de bildirimler için küçük resmi kullan Küçük resmi göster Android\'in bildirim rengini küçük resimdeki ana renge göre özelleştirmesini sağlayın (bunun tüm aygıtlarda kullanılamadığını unutmayın) Bildirimi renklendir + Dosya özeti hesaplanıyor + Video dosya özetleme süreci için bildirimler + Video Dosya Özeti Bildirimi \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles_services.xml b/app/src/main/res/values-v21/styles_services.xml index e5b675ef8bd..c495a9a31e8 100644 --- a/app/src/main/res/values-v21/styles_services.xml +++ b/app/src/main/res/values-v21/styles_services.xml @@ -12,6 +12,7 @@ + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/assets/NewPipe_background_just1.svg b/assets/NewPipe_background_just1.svg index dae3db6b32b..559a622f4a3 100644 --- a/assets/NewPipe_background_just1.svg +++ b/assets/NewPipe_background_just1.svg @@ -1,1252 +1,1252 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml index add17d42dbb..0a5190b295d 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -17,7 +17,7 @@ + lines="227,245"/>