diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..6419c65dd --- /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 1714c70d5..000000000 --- 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 new file mode 100644 index 000000000..a86eae8d9 --- /dev/null +++ b/README.ko.md @@ -0,0 +1,144 @@ +

+

NewPipe

+

A libre lightweight streaming frontend for Android.

+

+ +

+ + + + + + +

+
+

ScreenshotsDescriptionFeaturesUpdatesContributionDonateLicense

+

WebsiteBlogFAQPress

+
+ +*Read this in other languages: [English](README.md), [한국어](README.ko.md).* + +경고: 이 버전은 베타 버전이므로, 버그가 발생할 수도 있습니다. 만약 버그가 발생하였다면, 우리의 GITHUB 저장소에서 ISSUE를 열람하여 주십시오. + +NEWPIPE 또는 이것의 FORK을 구글 플레이스토어에 올리는 것은 그들의 이용약관을 위반합니다. + +## Screenshots + +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_01.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_02.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_03.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_04.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_05.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_06.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_07.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_08.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_09.png) +[](fastlane/metadata/android/en-US/images/phoneScreenshots/shot_10.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_11.png) +[](fastlane/metadata/android/en-US/images/tenInchScreenshots/shot_12.png) + +## Description + +NewPipe는 어떤 구글 프레임워크 라이브러리나, 유튜브 API를 사용하지 않습니다. 웹사이트는 단지 필요한 정보를 가져오기 위해 구문 분석 됩니다. 따라서 이 앱은 구글 서비스의 설치 없이 기기에서 사용될 수 있습니다. 또한, 카피레프트 자유 소프트웨어인 NewPipe를 사용하기 위해 유튜브 계정이 필요하지 않습니다. + +### Features + +* 영상 검색 +* 영상의 일반적인 정보 표시 +* 유튜브 영상 보기 +* 유튜브 영상 듣기 +* 팝업 모드 (floating player) +* 영상 공유 +* 영상 다운로드 +* 음성만 다운로드 +* Kodi에서 영상 열람 +* 다음/관련된 영상 표시 +* 특정 언어로 유튜브 검색 +* 연령 제한 컨텐츠 시청/차단 +* 채널에 대한 일반적인 정보 표시 +* 채널 검색 +* 채널에서 영상 시청 +* Orbot/Tor 지원 (아직 직접적이지 않음) +* 1080p/2K/4K 지원 +* 기록 보기 +* 채널 구독 +* 기록 검색 +* 재생목록 검색/시청 +* 추가된 재생목록 시청 +* 영상 추가 +* 지역 재생목록 +* 자막 +* 실시간 방송 지원 +* 댓글 표시 + +### Supported Services + +NewPipe는 여러가지 서비스를 지원합니다. 우리의 [문서](https://teamnewpipe.github.io/documentation/)는 새로운 서비스가 앱과 추출기에 어떻게 추가될 수 있는지에 대한 더 많은 정보를 제공합니다. 만약 새로운 서비스를 추가하고자 한다면, 우리에게 연락해 주시기 바랍니다. 현재 지원되는 서비스: + +* YouTube +* SoundCloud \[beta\] +* media.ccc.de \[beta\] +* PeerTube instances \[beta\] + +## Updates +NewPipe 코드의 변경이 있을 때(기능 추가 또는 버그 수정으로 인해), 결국 릴리즈가 발생할 것입니다. 이것들의 형식은 x.xx.x 입니다. +이 새로운 버전을 얻기 위해서, 당신은: + 1. 직접 디버그 APK를 생성할 수 있습니다. 이 방법은 당신의 기기에서 새로운 기능을 얻을 수 있는 가장 빠른 방법이지만, 꽤 많이 복잡합니다. + 따라서 우리는 다른 방법들 중 하나를 사용하는 것을 추천합니다. + 2. 우리의 커스텀 저장소를 F-Droid에 추가하고 우리가 릴리즈를 게시하는 대로 저곳에서 릴리즈를 설치할 수 있습니다. + 이에 대한 설명서는 이곳에서 확인할 수 있습니다: 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를 생성하고, 이것에 서명하고, 사용자들에서 업데이트를 전달해야만 하기 때문에, + 이것은 업데이트를 받는 가장 느린 방법입니다. + +우리는 대부분의 사용자에게 2번쨰 방법을 추천합니다. 방법 2 또는 3을 사용하여 설치된 APK는 서로 호환되지만, 방법 4를 사용하여 설치된 것들과는 호환되지 않습니다. 이것은 방법 2 또는 3에서는 같은 (우리의)서명 키가 사용되지만, 방법 4에서는 다른 (F-Droid의)서명 키가 사용되기 때문입니다. 방법 1을 사용하여 디버그 APK를 생성하는 것에서는 키가 완전히 제외됩니다. 서명 키는 사용자가 앱에 악의적인 업데이트를 설치하는 것에 대해 속지 않도록 보장하는 것을 도와줍니다. + +한편, 만약 어떠한 이유(예. NewPipe의 핵심 기능이 손상되었고 F-Droid가 아직 업데이트를 가지지 않는 경우) 때문에 소스를 바꾸길 원한다면, +우리는 다음과 같은 절차를 따르는 것을 권장합니다: +1. 당신의 기록, 구독, 그리고 재생목록을 유지할 수 있도록 Settings > Content > Export Database 를 통해 데이터를 백업하십시오. +2. NewPipe를 삭제하십시오. +3. 새로운 소스에서 APK를 다운로드하고 이것을 설치하십시오. +4. Step 1의 Settings > Content > Export Database 을 통해 데이터를 불러오십시오. + +## Contribution +당신이 아이디어, 번역, 디자인 변경, 코드 정리, 또는 정말 큰 코드 수정에 대한 의견이 있다면, 도움은 항상 환영합니다. +더 많이 수행될수록 더 많이 발전할 수 있습니다! + +만약 참여하고 싶다면, 우리의 [컨트리뷰션 공지](.github/CONTRIBUTING.md)를 참고하십시오. + + +Translation status + + +## Donate +만약 NewPipe가 마음에 들었다면, 우리는 기부에 대해 기꺼이 환영합니다. bitcoin을 보내거나, Bountysource 또는 Liberapay를 통해 기부할 수 있습니다. NewPipe에 기부하는 것에 대한 자세한 정보를 원한다면, 우리의 [웹사이트](https://newpipe.net/donate)를 방문하여 주십시오. + + + + + + + + + + + + + + + + + +
BitcoinBitcoin QR code16A9J59ahMRqkLSZjhYj33n9j3fMztFxnh
LiberapayVisit NewPipe at liberapay.comDonate via Liberapay
BountysourceVisit NewPipe at bountysource.comCheck out how many bounties you can earn.
+ +## Privacy Policy + +NewPipe 프로젝트는 미디어 웹 서비스를 사용하는 것에 대한 사적의, 익명의 경험을 제공하는 것을 목표로 하고 있습니다. +그러므로, 앱은 당신의 동의 없이 어떤 데이터도 수집하지 않습니다. 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) + +NewPipe는 자유 소프트웨어입니다: 당신의 마음대로 이것을 사용하고, 연구하고, 공유하고, 개선할 수 있습니다. +구체적으로 당신은 자유 소프트웨어 재단에서 발행되는, 버전 3 또는 (당신의 선택에 따라)이후 버전의, +[GNU General Public License](https://www.gnu.org/licenses/gpl.html) 하에서 이것을 재배포 및/또는 수정할 수 있습니다. diff --git a/app/build.gradle b/app/build.gradle index 219fda860..d45c5727a 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_r3" + 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 1b9de5620..d240d123f 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 de401d4f5..e6dce4d67 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 @@ public class App extends MultiDexApplication { 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 @@ public class App extends MultiDexApplication { 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/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index f51ecf2d3..0c784e9d5 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.View; 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.fragment.app.FragmentManager; 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 @@ public class MainActivity extends AppCompatActivity { + "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 @@ public class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { } 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 @@ public class MainActivity extends AppCompatActivity { 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 class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { } 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 @@ public class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { } 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 @@ public class MainActivity extends AppCompatActivity { 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 class MainActivity extends AppCompatActivity { } 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 class MainActivity extends AppCompatActivity { } 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 @@ public class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { // 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 @@ public class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { 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 class MainActivity extends AppCompatActivity { } 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 class MainActivity extends AppCompatActivity { // 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 class MainActivity extends AppCompatActivity { 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 @@ public class MainActivity extends AppCompatActivity { 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 class MainActivity extends AppCompatActivity { } 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 c962ed99d..463fc24ac 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.util.Log; 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 class ReCaptchaActivity extends AppCompatActivity { }); // 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 class ReCaptchaActivity extends AppCompatActivity { @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 9ad993de1..98a0921e4 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.LayoutInflater; 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.AlertDialog; 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 @@ public class RouterActivity extends AppCompatActivity { 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 @@ public class RouterActivity extends AppCompatActivity { 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 class RouterActivity extends AppCompatActivity { @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 e3e56816c..6ff691561 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.LayoutInflater; 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 class AboutActivity extends AppCompatActivity { } @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 d9c892099..aff6205f2 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 979f8be75..37eefed96 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 android.view.ViewTreeObserver; 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 @@ public class DownloadActivity extends AppCompatActivity { assureCorrectAppLanguage(this); ThemeHelper.setTheme(this); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_downloader); - final Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + super.onCreate(savedInstanceState); + + 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 866b324ec..a77109f86 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 @@ package org.schabi.newpipe.fragments; 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.appcompat.app.AppCompatActivity; 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 5b2ee5473..77ceb4a51 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.Bundle; 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; @@ -124,12 +123,14 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; 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 @@ -221,6 +222,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; @@ -512,8 +516,8 @@ public final class VideoDetailFragment } 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()); } @@ -587,7 +591,7 @@ public final class VideoDetailFragment } 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 { @@ -648,6 +652,9 @@ public final class VideoDetailFragment 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); @@ -752,7 +759,7 @@ public final class VideoDetailFragment 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 @@ -767,12 +774,12 @@ public final class VideoDetailFragment 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); } @@ -1258,7 +1265,7 @@ public final class VideoDetailFragment } private void prepareDescription(final Description description) { - if (description == null || TextUtils.isEmpty(description.getContent()) + if (description == null || isEmpty(description.getContent()) || description == Description.emptyDescription) { return; } @@ -1503,9 +1510,9 @@ public final class VideoDetailFragment 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); @@ -1600,6 +1607,8 @@ public final class VideoDetailFragment prepareDescription(info.getDescription()); updateProgressInfo(info); initThumbnailViews(info); + showMetaInfoInTextView(info.getMetaInfo(), detailMetaInfoTextView, detailMetaInfoSeparator); + if (player == null || player.isPlayerStopped()) { updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); @@ -1651,7 +1660,7 @@ public final class VideoDetailFragment 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); @@ -2346,10 +2355,10 @@ public final class VideoDetailFragment 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 d1a964fb2..6fa7eb700 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.MenuInflater; 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.database.playlist.model.PlaylistRemoteEntity; 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.Localization; 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.CompositeDisposable; 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 class PlaylistFragment extends BaseListInfoFragment { 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 @@ public class PlaylistFragment extends BaseListInfoFragment { 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 02dbf176b..2dac6d11b 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.ReCaptchaActivity; 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 io.reactivex.rxjava3.subjects.PublishSubject; 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 ddbbea23d..2a0aa1c90 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 73c0d23a0..34543b565 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.Nullable; 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 @@ public abstract class BaseImportExportService extends Service { 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 860ace84c..c1c2e4eba 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.Nullable; 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 @@ public final class NotificationUtil { } 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 d1c2be014..fd20fd175 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.Menu; 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.PlaybackParameters; 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity protected void onResume() { super.onResume(); if (redraw) { - recreate(); + ActivityCompat.recreate(this); redraw = false; } } @@ -229,14 +207,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity } 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 abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 abstract class ServicePlayerActivity extends AppCompatActivity 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 abstract class ServicePlayerActivity extends AppCompatActivity 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 abstract class ServicePlayerActivity extends AppCompatActivity if (player != null) { player.seekTo(seekBar.getProgress()); } - seekDisplay.setVisibility(View.GONE); + queueControlBinding.seekDisplay.setVisibility(View.GONE); seeking = false; } @@ -601,45 +557,46 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity } 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 @@ public abstract class ServicePlayerActivity extends AppCompatActivity //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 080c89e3f..81d1bcefd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -106,6 +106,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +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; @@ -967,10 +968,17 @@ public class VideoPlayerImpl extends VideoPlayer 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 bb1f71b29..655e11e00 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.Menu; 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.ActivityCommunicator; 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 @@ public class ErrorActivity extends AppCompatActivity { 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 @@ public class ErrorActivity extends AppCompatActivity { 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 @@ public class ErrorActivity extends AppCompatActivity { 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 @@ public class ErrorActivity extends AppCompatActivity { } 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 @@ public class ErrorActivity extends AppCompatActivity { + BuildConfig.VERSION_NAME + "\n" + getOsString(); - infoView.setText(text); + activityErrorBinding.errorInfosView.setText(text); } private String buildJson() { @@ -374,7 +362,8 @@ public class ErrorActivity extends AppCompatActivity { .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 @@ public class ErrorActivity extends AppCompatActivity { 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 @@ public class ErrorActivity extends AppCompatActivity { 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 ab875ed5d..8126bd2c5 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.provider.Settings; 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 class AppearanceSettingsFragment extends BasePreferenceFragment { 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 9126cdd58..c9a76a084 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 android.widget.Toast; 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.report.UserAction; 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; @@ -124,17 +123,18 @@ public class ContentSettingsFragment extends BasePreferenceFragment { @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { + 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"); - 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"); + 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)); @@ -216,33 +216,16 @@ public class ContentSettingsFragment extends BasePreferenceFragment { //checkpoint before export NewPipeDatabase.checkpoint(); - try (ZipOutputStream outZip = new ZipOutputStream(new BufferedOutputStream( - new FileOutputStream(path)))) { - ZipHelper.addFileToZip(outZip, newpipeDb.getPath(), "newpipe.db"); + final SharedPreferences preferences = PreferenceManager + .getDefaultSharedPreferences(requireContext()); + manager.exportDatabase(preferences, path); - saveSharedPreferencesToFile(newpipeSettings); - ZipHelper.addFileToZip(outZip, newpipeSettings.getPath(), - "newpipe.settings"); - } - - 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 { 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 000000000..2682ac5e0 --- /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 d2d4c2404..4de166a55 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 android.view.MenuItem; 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 @@ public class SettingsActivity extends AppCompatActivity 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 5efffe118..ca3da9d24 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 @@ public class Mp4FromDashWriter { // 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 650c5ae11..1f1b94545 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 @@ package org.schabi.newpipe.util; 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.Info; 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 java.util.List; 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 final class ExtractorHelper { } }); } + + /** + * 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 710827864..978f558c4 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 @@ import java.util.Locale; 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 b43733a51..e77196445 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.IBinder; 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 class DownloadManagerService extends Service { 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 class DownloadManagerService extends Service { 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 be7d78299..f102206c1 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 android.widget.Toast; 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 @@ public class MissionAdapter extends Adapter implements Handler.Callb 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 13e3c1b5f..d90c782ef 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 ed651b17a..f52d0d996 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 0df85fe95..758a88f19 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 d50924c46..32c6c6b91 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 14bbb79a9..efd682d9e 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 8076d279f..be4aa932e 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 812a1a815..6a8f4c212 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 73ec90c82..7a8611d1c 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 3fde293d8..a33036846 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 fbfe8eeda..05e4d955c 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 6effa7615..2ec4579f6 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 5410adf55..c7b189318 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 fe16a89bc..2cf9f3b01 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 a477cb27c..0dea45178 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 c9026aded..baa0e8084 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 4b04865bd..c128c4214 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 8da1918d3..e366a673c 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 063686d8f..0146b5fff 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 4e23cce03..b76274305 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 9a3f6ef4e..86d125b70 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 e9253598d..34df2592e 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 a9ed08379..36ed9d665 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 0217368da..e120750fc 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 d4ee389f2..231702c24 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 c0e61b392..b9c352e79 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 9c4000230..4799c6ea0 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 c672c648f..433bd50fb 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 d04b2be1c..f4827ca77 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 2127434b6..8f9198d3f 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 e08ab1b7b..9f1639644 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 000000000..0ae0fc31a --- /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 a65e26394..243f77f11 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 bc2b4ba5d..d105be597 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 e5b675ef8..c495a9a31 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 dae3db6b3..559a622f4 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 add17d42d..0a5190b29 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -17,7 +17,7 @@ + lines="227,245"/>