1.1.0
- removed programmatical creation of cards and instead inflated them through layouts - created shimmer card effects when cards are loading from the api - made api calls in threads so that the app won't hang loading tons of data - made buttons adapt to Material U colors - made result cards show which videos are downloaded already by changing the download icons - showed video duration in result files - added trending videos if results are empty, based on your location - made the cards 16:9 no all devices - removed old menubar and now it blends with the statusbar - When switching to light mode, the status bar now changes as expected - added download progress notification - removed unnecessary toasts - fixed card scrolling when downloading a playlist - added floating action button to scroll to top - made appbar clickable that will also scroll to top - made bottom nav bar buttons scroll to top if you are clicking the highlighted button - fixed text sizes and positions - removed youtubedl-android folders and instead used modules (it now works, previously i had to manually add them) - removed hard coded strings which most were albanian. Now they are english by default and if you change the language in your phone's settings, it changes language too. Currently the app supports only English and Albanian - and other small fixes and details
This commit is contained in:
parent
a0be8c7f99
commit
2b28f3472b
95 changed files with 1831 additions and 2241 deletions
|
|
@ -23,7 +23,7 @@
|
|||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/keyboard_arrow_down/baseline_keyboard_arrow_down_24.xml" />
|
||||
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/announcement/baseline_announcement_24.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
|
|
@ -33,8 +33,8 @@
|
|||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="ff4d00" />
|
||||
<entry key="outputName" value="ic_download" />
|
||||
<entry key="color" value="00a6ff" />
|
||||
<entry key="outputName" value="ic_announcement" />
|
||||
<entry key="sourceFile" value="C:\Users\denis\Desktop\youtubedl-android-0.13.1\youtubedl-android-0.13.1" />
|
||||
</map>
|
||||
</option>
|
||||
|
|
|
|||
|
|
@ -21,5 +21,10 @@
|
|||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -35,6 +35,31 @@
|
|||
<entry key="..\:/Users/denis/Desktop/ytdlnis/ytdlnis/app/src/main/res/menu/main_menu.xml" value="0.2057460611677479" />
|
||||
<entry key="..\:/Users/denis/Desktop/ytdlnis/ytdlnis/app/src/main/res/xml/root_preferences.xml" value="0.33" />
|
||||
<entry key="..\:/Users/denis/Desktop/ytdlnis/ytdlnis/app/src/main/res/xml/shortcuts.xml" value="0.13852073535699017" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_announcement.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_cloud_download.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_download.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_go_up.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_home.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_music.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_music_downloaded.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_search.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_video.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/activity_main.xml" value="0.18" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/download_all_card.xml" value="0.3375" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/empty_history_hint.xml" value="0.2" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_history.xml" value="0.20555555555555555" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_home.xml" value="0.1" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_settings.xml" value="0.16875" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/history_card.xml" value="0.1" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/history_card_shimmer.xml" value="0.22" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/result_card.xml" value="0.33" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/result_card_shimmer.xml" value="0.1" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/search_result.xml" value="0.33" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/menu/bottom_nav_menu.xml" value="0.20555555555555555" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/menu/history_menu.xml" value="0.16875" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/menu/main_menu.xml" value="0.16875" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/menu/settings_menu.xml" value="0.16875" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/xml/root_preferences.xml" value="0.30833333333333335" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ android {
|
|||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
compose true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
|
@ -35,6 +36,7 @@ android {
|
|||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
|
|
@ -50,15 +52,19 @@ android {
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation project(":library")
|
||||
implementation project(":ffmpeg")
|
||||
// implementation project(":library")
|
||||
// implementation project(":ffmpeg")
|
||||
|
||||
implementation 'com.github.yausername.youtubedl-android:library:0.13.3'
|
||||
implementation 'com.github.yausername.youtubedl-android:ffmpeg:0.13.3'
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.core:core:1.8.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
testImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
||||
|
|
@ -69,4 +75,7 @@ dependencies {
|
|||
implementation("androidx.cardview:cardview:1.0.0")
|
||||
implementation("com.squareup.picasso:picasso:2.8")
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||
implementation 'com.facebook.shimmer:shimmer:0.5.0'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.devbrackets.android.exomedia.BuildConfig;
|
||||
import com.google.android.material.color.DynamicColors;
|
||||
import com.yausername.ffmpeg.FFmpeg;
|
||||
import com.yausername.youtubedl_android.YoutubeDL;
|
||||
import com.yausername.youtubedl_android.YoutubeDLException;
|
||||
|
|
@ -19,12 +25,15 @@ import io.reactivex.schedulers.Schedulers;
|
|||
public class App extends Application {
|
||||
|
||||
private static final String TAG = "App";
|
||||
public static final String DOWNLOAD_CHANNEL_ID = "1";
|
||||
ActivityMainBinding binding;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
createNotificationChannel();
|
||||
configureRxJavaErrorHandler();
|
||||
Completable.fromAction(this::initLibraries).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
|
||||
@Override
|
||||
|
|
@ -61,4 +70,16 @@ public class App extends Application {
|
|||
YoutubeDL.getInstance().init(this);
|
||||
FFmpeg.getInstance().init(this);
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = getString(R.string.download_notification_channel_name);
|
||||
String description = getString(R.string.download_notification_channel_description);
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
NotificationChannel channel = new NotificationChannel(DOWNLOAD_CHANNEL_ID, name, importance);
|
||||
channel.setDescription(description);
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,17 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
|
@ -31,36 +25,39 @@ import android.view.ViewGroup;
|
|||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.deniscerri.ytdl.database.DBManager;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
import com.deniscerri.ytdl.placeholder.PlaceholderContent;
|
||||
import com.facebook.shimmer.ShimmerFrameLayout;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
*/
|
||||
public class HistoryFragment extends Fragment {
|
||||
public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||
|
||||
private LinearLayout linearLayout;
|
||||
private ScrollView scrollView;
|
||||
private View fragmentView;
|
||||
private DBManager dbManager;
|
||||
private ArrayList<Video> historyObjects;
|
||||
Context context;
|
||||
private LayoutInflater layoutinflater;
|
||||
private ShimmerFrameLayout shimmerCards;
|
||||
private MaterialToolbar topAppBar;
|
||||
|
||||
private static final String TAG = "HistoryFragment";
|
||||
|
||||
|
||||
public HistoryFragment() {
|
||||
}
|
||||
|
||||
// TODO: Customize parameter initialization
|
||||
@SuppressWarnings("unused")
|
||||
public static HistoryFragment newInstance() {
|
||||
HistoryFragment fragment = new HistoryFragment();
|
||||
|
|
@ -79,163 +76,135 @@ public class HistoryFragment extends Fragment {
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
fragmentView = inflater.inflate(R.layout.fragment_history, container, false);
|
||||
initViews();
|
||||
context = fragmentView.getContext();
|
||||
layoutinflater = LayoutInflater.from(context);
|
||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
||||
shimmerCards = fragmentView.findViewById(R.id.shimmer_history_framelayout);
|
||||
topAppBar = fragmentView.findViewById(R.id.history_toolbar);
|
||||
|
||||
dbManager = new DBManager(requireContext());
|
||||
initCards();
|
||||
dbManager = new DBManager(context);
|
||||
|
||||
SwipeRefreshLayout swipeRefreshLayout = fragmentView.findViewById(R.id.swiperefresh);
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
initCards();
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
});
|
||||
|
||||
|
||||
FloatingActionButton fab = fragmentView.findViewById(R.id.fab_history);
|
||||
fab.setOnClickListener(this);
|
||||
scrollView.setOnScrollChangeListener((view, x, y, oldX, oldY) -> {
|
||||
if( y > 500){
|
||||
fab.show();
|
||||
}else{
|
||||
fab.hide();
|
||||
}
|
||||
});
|
||||
initMenu();
|
||||
initCards();
|
||||
return fragmentView;
|
||||
}
|
||||
|
||||
|
||||
private void initCards(){
|
||||
shimmerCards.startShimmer();
|
||||
shimmerCards.setVisibility(View.VISIBLE);
|
||||
linearLayout.removeAllViews();
|
||||
historyObjects = dbManager.merrHistorine();
|
||||
for(int i = historyObjects.size()-1; i >= 0; i--){
|
||||
createCard(historyObjects.get(i));
|
||||
try{
|
||||
Thread thread = new Thread(() -> {
|
||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
ArrayList<Video> historyObjects = dbManager.getHistory();
|
||||
for(int i = historyObjects.size()-1; i >= 0; i--){
|
||||
createCard(historyObjects.get(i));
|
||||
}
|
||||
TextView padding = new TextView(context);
|
||||
int dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, context.getResources().getDisplayMetrics());
|
||||
padding.setHeight(dp);
|
||||
padding.setGravity(Gravity.CENTER);
|
||||
|
||||
uiHandler.post(() -> {
|
||||
linearLayout.addView(padding);
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
});
|
||||
});
|
||||
thread.start();
|
||||
}catch(Exception e){
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getDp(int value){
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
|
||||
public void scrollToTop(){
|
||||
scrollView.smoothScrollTo(0,0);
|
||||
}
|
||||
|
||||
private void createCard(Video video) {
|
||||
|
||||
RelativeLayout r = new RelativeLayout(getContext());
|
||||
r.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
|
||||
r.getLayoutParams().height = getDp(130);
|
||||
|
||||
CardView card = new CardView(requireContext());
|
||||
card.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
card.setRadius(getDp(10));
|
||||
card.setCardElevation(10);
|
||||
card.setMaxCardElevation(12);
|
||||
card.setPreventCornerOverlap(true);
|
||||
card.setUseCompatPadding(true);
|
||||
card.setCardBackgroundColor(ContextCompat.getColor(requireContext(), R.color.colorPrimaryDark));
|
||||
private void createCard(Video video){
|
||||
RelativeLayout r = new RelativeLayout(context);
|
||||
layoutinflater.inflate(R.layout.history_card, r);
|
||||
|
||||
CardView card = r.findViewById(R.id.history_card_view);
|
||||
// THUMBNAIL ----------------------------------
|
||||
|
||||
ImageView thumbnail = new ImageView(getContext());
|
||||
thumbnail.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
ImageView thumbnail = card.findViewById(R.id.history_image_view);
|
||||
String imageURL= video.getThumb();
|
||||
|
||||
|
||||
Picasso.get().load(imageURL).into(thumbnail);
|
||||
thumbnail.setAdjustViewBounds(false);
|
||||
thumbnail.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
uiHandler.post(() -> Picasso.get().load(imageURL).into(thumbnail));
|
||||
thumbnail.setColorFilter(Color.argb(70, 0, 0, 0));
|
||||
|
||||
// TITLE ----------------------------------
|
||||
TextView videoTitle = new TextView(getContext());
|
||||
videoTitle.setLayoutParams(new RelativeLayout.LayoutParams(getDp(300), getDp(100)));
|
||||
int padding = getDp(20);
|
||||
videoTitle.setPadding(padding, padding, padding, padding);
|
||||
TextView videoTitle = card.findViewById(R.id.history_title);
|
||||
String title = video.getTitle();
|
||||
|
||||
videoTitle.setText(video.getTitle());
|
||||
videoTitle.setTextSize(getDp(5));
|
||||
videoTitle.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
|
||||
videoTitle.setTypeface(null, Typeface.BOLD);
|
||||
videoTitle.setShadowLayer(2f, 4f, 4f, ContextCompat.getColor(requireContext(), R.color.black));
|
||||
if(title.length() > 100){
|
||||
title = title.substring(0, 40) + "...";
|
||||
}
|
||||
videoTitle.setText(title);
|
||||
|
||||
// AUTHOR ----------------------------------
|
||||
// Bottom Info ----------------------------------
|
||||
TextView bottomInfo = card.findViewById(R.id.history_info_bottom);
|
||||
String info = video.getAuthor() + " • " + video.getDuration();
|
||||
bottomInfo.setText(info);
|
||||
|
||||
TextView videoAuthor = new TextView(getContext());
|
||||
videoAuthor.setGravity(Gravity.BOTTOM);
|
||||
videoAuthor.setLayoutParams(new RelativeLayout.LayoutParams(getDp(150), getDp(100)));
|
||||
videoAuthor.setPadding(getDp(20), 0, 0, getDp(10));
|
||||
|
||||
videoAuthor.setText(video.getAuthor());
|
||||
videoAuthor.setTextSize(getDp(3));
|
||||
videoAuthor.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
|
||||
videoAuthor.setShadowLayer(2f, 4f, 4f, ContextCompat.getColor(requireContext(), R.color.black));
|
||||
videoAuthor.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
// DATE ---------------------------------------------
|
||||
|
||||
TextView date = new TextView(getContext());
|
||||
date.setGravity(Gravity.BOTTOM);
|
||||
date.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, getDp(100)));
|
||||
date.setPadding(getDp(250), 0, getDp(20), getDp(10));
|
||||
date.setShadowLayer(1.5f, 4f, 4f, R.color.black);
|
||||
|
||||
date.setText(video.getDownloadedTime());
|
||||
date.setTextSize(getDp(3));
|
||||
date.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
|
||||
date.setShadowLayer(1, 1, 1, ContextCompat.getColor(requireContext(), R.color.colorPrimaryDark));
|
||||
date.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
// BUTTONS -------------------------------------------
|
||||
|
||||
|
||||
LinearLayout buttonLayout = new LinearLayout(requireContext());
|
||||
buttonLayout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
|
||||
buttonLayout.setGravity(Gravity.BOTTOM | Gravity.RIGHT);
|
||||
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
buttonLayout.setPadding(getDp(10), 0, getDp(20), getDp(40));
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
getDp(48), getDp(48)
|
||||
);
|
||||
// TIME DOWNLOADED ----------------------------------
|
||||
TextView datetime = card.findViewById(R.id.history_info_time);
|
||||
String downloadedTime = video.getDownloadedTime();
|
||||
datetime.setText(downloadedTime);
|
||||
|
||||
// BUTTON ----------------------------------
|
||||
LinearLayout buttonLayout = card.findViewById(R.id.history_download_button_layout);
|
||||
Button btn = buttonLayout.findViewById(R.id.history_download_button_type);
|
||||
if(video.getDownloadedType().equals("mp3")){
|
||||
Button musicBtn = new Button(getContext());
|
||||
|
||||
params.setMargins(0,0, getDp(10), 0);
|
||||
musicBtn.setLayoutParams(params);
|
||||
//musicBtn.setIconSize(getDp(24));
|
||||
musicBtn.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.ic_music));
|
||||
|
||||
buttonLayout.addView(musicBtn);
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_music));
|
||||
}else{
|
||||
Button videoBtn = new Button(requireContext());
|
||||
videoBtn.setLayoutParams(params);
|
||||
//videoBtn.setIconSize(getDp(24));
|
||||
videoBtn.setBackground(ContextCompat.getDrawable(requireContext(), R.drawable.ic_video));
|
||||
|
||||
buttonLayout.addView(videoBtn);
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_video));
|
||||
}
|
||||
|
||||
card.addView(thumbnail);
|
||||
card.addView(videoTitle);
|
||||
card.addView(videoAuthor);
|
||||
card.addView(date);
|
||||
card.addView(buttonLayout);
|
||||
|
||||
r.addView(card);
|
||||
|
||||
linearLayout.addView(r);
|
||||
uiHandler.post(() -> linearLayout.addView(r));
|
||||
}
|
||||
|
||||
private void initMenu(){
|
||||
topAppBar.setOnClickListener(view -> {
|
||||
scrollToTop();
|
||||
});
|
||||
|
||||
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
||||
switch (m.getItemId()){
|
||||
case R.id.delete_history:
|
||||
dbManager.clearHistory();
|
||||
linearLayout.removeAllViews();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
private void initViews() {
|
||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
inflater.inflate(R.menu.history_menu, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.delete_history:
|
||||
dbManager.clearHistory();
|
||||
linearLayout.removeAllViews();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.fab_history) {
|
||||
scrollView.smoothScrollBy(0, 0);
|
||||
scrollView.smoothScrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,19 +1,12 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
|
||||
|
|
@ -34,30 +27,6 @@ public class MainActivity extends AppCompatActivity{
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||
this.setTheme(R.style.AppTheme);
|
||||
|
||||
ColorStateList list = new ColorStateList(
|
||||
new int[][]{
|
||||
new int[]{}
|
||||
},
|
||||
new int[]{
|
||||
ContextCompat.getColor(getApplicationContext(), R.color.material_dynamic_primary50)
|
||||
}
|
||||
);
|
||||
binding.bottomNavigationView.setItemActiveIndicatorColor(list);
|
||||
binding.bottomNavigationView.setItemRippleColor(list);
|
||||
|
||||
int nightMode = getApplicationContext().getResources().getConfiguration().uiMode &
|
||||
Configuration.UI_MODE_NIGHT_MASK;
|
||||
if(nightMode == Configuration.UI_MODE_NIGHT_YES){
|
||||
binding.bottomNavigationView.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.material_dynamic_neutral10));
|
||||
}
|
||||
|
||||
getWindow().setStatusBarColor(ContextCompat.getColor(getApplicationContext(), R.color.material_dynamic_neutral10));
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
|
@ -85,26 +54,28 @@ public class MainActivity extends AppCompatActivity{
|
|||
if(lastFragment == homeFragment){
|
||||
homeFragment.scrollToTop();
|
||||
}else{
|
||||
this.setTitle("YTDLnis");;
|
||||
this.setTitle(R.string.app_name);;
|
||||
}
|
||||
replaceFragment(homeFragment);
|
||||
break;
|
||||
case R.id.history:
|
||||
this.setTitle("Historia");;
|
||||
if(lastFragment == historyFragment){
|
||||
historyFragment.scrollToTop();
|
||||
}else {
|
||||
this.setTitle(getString(R.string.history));
|
||||
}
|
||||
replaceFragment(historyFragment);
|
||||
break;
|
||||
case R.id.settings:
|
||||
this.setTitle("Cilësimet");;
|
||||
this.setTitle(getString(R.string.settings));;
|
||||
replaceFragment(settingsFragment);
|
||||
break;
|
||||
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Intent intent = getIntent();
|
||||
handleIntents(intent);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.yausername.youtubedl_android.YoutubeDL;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
|
@ -36,15 +31,15 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
public static boolean updating;
|
||||
|
||||
private static final String TAG = "SettingsFragment";
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
|
||||
musicPath = (Preference) findPreference("music_path");
|
||||
videoPath = (Preference) findPreference("video_path");
|
||||
musicPath = findPreference("music_path");
|
||||
videoPath = findPreference("video_path");
|
||||
|
||||
SharedPreferences preferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
|
||||
String music_path = preferences.getString("music_path", "");
|
||||
|
|
@ -59,7 +54,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
musicPath.setSummary(music_path);
|
||||
musicPath.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
startActivityForResult(intent, MUSIC_PATH_CODE);
|
||||
musicPathResultLauncher.launch(intent);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
|
@ -73,11 +68,11 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
videoPath.setSummary(video_path);
|
||||
videoPath.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
startActivityForResult(intent, VIDEO_PATH_CODE);
|
||||
videoPathResultLauncher.launch(intent);
|
||||
return true;
|
||||
});
|
||||
|
||||
update = (Preference) findPreference("update");
|
||||
update = findPreference("update");
|
||||
if(update != null){
|
||||
update.setOnPreferenceClickListener(preference -> {
|
||||
updateYoutubeDL();
|
||||
|
|
@ -88,23 +83,27 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case MUSIC_PATH_CODE:
|
||||
if(resultCode == Activity.RESULT_OK){
|
||||
changePath(musicPath, data, requestCode);
|
||||
ActivityResultLauncher<Intent> musicPathResultLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
new ActivityResultCallback<ActivityResult>() {
|
||||
@Override
|
||||
public void onActivityResult(ActivityResult result) {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
changePath(musicPath, result.getData(), MUSIC_PATH_CODE);
|
||||
}
|
||||
break;
|
||||
case VIDEO_PATH_CODE:
|
||||
if(resultCode == Activity.RESULT_OK){
|
||||
changePath(videoPath, data, requestCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
ActivityResultLauncher<Intent> videoPathResultLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
new ActivityResultCallback<ActivityResult>() {
|
||||
@Override
|
||||
public void onActivityResult(ActivityResult result) {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
changePath(videoPath, result.getData(), VIDEO_PATH_CODE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public void changePath(Preference p, Intent data, int requestCode){
|
||||
Log.e("TEST", data.toUri(0));
|
||||
|
|
|
|||
|
|
@ -1,41 +1,43 @@
|
|||
package com.deniscerri.ytdl.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.deniscerri.ytdl.database.DBManager;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.sql.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
public class YoutubeAPIManager {
|
||||
private static final String TAG = "API MANAGER";
|
||||
private static String countryCODE = "US";
|
||||
private ArrayList<Video> videos;
|
||||
private String key;
|
||||
Properties properties = new Properties();
|
||||
private DBManager dbManager;
|
||||
|
||||
public YoutubeAPIManager(Context context) {
|
||||
AssetManager assetManager = context.getAssets();
|
||||
@Nullable ApplicationInfo applicationInfo;
|
||||
try{
|
||||
InputStream inputStream = assetManager.open("config.properties");
|
||||
properties.load(inputStream);
|
||||
key = properties.getProperty("ytAPI");
|
||||
applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
||||
key = applicationInfo.metaData.getString("ytAPIkey");
|
||||
dbManager = new DBManager(context);
|
||||
|
||||
//get Locale
|
||||
JSONObject country = genericRequest("https://ipwho.is/");
|
||||
try{
|
||||
countryCODE = country.getString("country_code");
|
||||
}catch(Exception ignored){}
|
||||
|
||||
}catch(Exception ignored){
|
||||
Toast.makeText(context, "Couldn't find API Key for the request", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
|
@ -44,48 +46,90 @@ public class YoutubeAPIManager {
|
|||
}
|
||||
|
||||
public ArrayList<Video> search(String query) throws JSONException{
|
||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25®ionCode=US&key="+key);
|
||||
//short data
|
||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25®ionCode="+countryCODE+"&key="+key);
|
||||
JSONArray dataArray = res.getJSONArray("items");
|
||||
|
||||
//extra data
|
||||
String url2 = "https://www.googleapis.com/youtube/v3/videos?id=";
|
||||
//getting all ids, for the extra data request
|
||||
for(int i = 0; i < dataArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
|
||||
if(element.getJSONObject("id").getString("kind").equals("youtube#video")){
|
||||
String videoID = element.getJSONObject("id").getString("videoId");
|
||||
url2 = url2 + videoID + ",";
|
||||
snippet.put("videoID", videoID);
|
||||
}
|
||||
}
|
||||
url2 = url2.substring(0, url2.length()-1) + "&part=contentDetails®ionCode="+countryCODE+"&key="+key;
|
||||
JSONObject extra = genericRequest(url2);
|
||||
int j = 0;
|
||||
for(int i = 0; i < dataArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
if(element.getJSONObject("id").getString("kind").equals("youtube#video")){
|
||||
snippet.put("videoID", element.getJSONObject("id").getString("videoId"));
|
||||
snippet = fixThumbnail(snippet);
|
||||
Video v = createVideofromJSON(snippet);
|
||||
v.setTitle(v.getTitle().replace("&", "&").replace(""", "\""));
|
||||
v.setAuthor(v.getAuthor().replace("&", "&").replace(""", "\""));
|
||||
videos.add(createVideofromJSON(snippet));
|
||||
}
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
String duration = extra.getJSONArray("items").getJSONObject(j++).getJSONObject("contentDetails").getString("duration");
|
||||
duration = formatDuration(duration);
|
||||
if(duration.equals("0:00")){
|
||||
continue;
|
||||
}
|
||||
|
||||
public ArrayList<Video> getPlaylist(String id, String nextPageToken) throws JSONException{
|
||||
String url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken="+nextPageToken+"&maxResults=50&playlistId="+id+"&key="+key;
|
||||
JSONObject res = genericRequest(url);
|
||||
JSONArray dataArray = res.getJSONArray("items");
|
||||
|
||||
for(int i = 0; i < dataArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
if(snippet.getJSONObject("resourceId").getString("kind").equals("youtube#video")){
|
||||
snippet.put("videoID", snippet.getJSONObject("resourceId").getString("videoId"));
|
||||
snippet.put("duration", duration);
|
||||
snippet = fixThumbnail(snippet);
|
||||
Video v = createVideofromJSON(snippet);
|
||||
|
||||
if(v.getThumb().isEmpty()){
|
||||
continue;
|
||||
}
|
||||
|
||||
v.setIsPlaylistItem(1);
|
||||
v.setTitle(v.getTitle().replace("&", "&").replace(""", "\""));
|
||||
v.setAuthor(v.getAuthor().replace("&", "&").replace(""", "\""));
|
||||
|
||||
videos.add(v);
|
||||
|
||||
videos.add(createVideofromJSON(snippet));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
public ArrayList<Video> getPlaylist(String id, String nextPageToken) throws JSONException{
|
||||
String url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken="+nextPageToken+"&maxResults=50®ionCode="+countryCODE+"&playlistId="+id+"&key="+key;
|
||||
//short data
|
||||
JSONObject res = genericRequest(url);
|
||||
JSONArray dataArray = res.getJSONArray("items");
|
||||
|
||||
//extra data
|
||||
String url2 = "https://www.googleapis.com/youtube/v3/videos?id=";
|
||||
//getting all ids, for the extra data request
|
||||
for(int i = 0; i < dataArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
String videoID = snippet.getJSONObject("resourceId").getString("videoId");
|
||||
url2 = url2 + videoID + ",";
|
||||
snippet.put("videoID", videoID);
|
||||
}
|
||||
url2 = url2.substring(0, url2.length()-1) + "&part=contentDetails®ionCode="+countryCODE+"&key="+key;
|
||||
JSONObject extra = genericRequest(url2);
|
||||
JSONArray extraArray = extra.getJSONArray("items");
|
||||
int j = 0;
|
||||
int i;
|
||||
for(i = 0; i < extraArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
String duration = extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails").getString("duration");
|
||||
duration = formatDuration(duration);
|
||||
snippet.put("duration", duration);
|
||||
snippet = fixThumbnail(snippet);
|
||||
Video v = createVideofromJSON(snippet);
|
||||
|
||||
if(v.getThumb().isEmpty()){
|
||||
continue;
|
||||
}else{
|
||||
j++;
|
||||
}
|
||||
|
||||
v.setIsPlaylistItem(1);
|
||||
videos.add(v);
|
||||
}
|
||||
String next = res.optString("nextPageToken");
|
||||
if(next != ""){
|
||||
ArrayList<Video> nextPage = getPlaylist(id,next);
|
||||
|
|
@ -96,16 +140,18 @@ public class YoutubeAPIManager {
|
|||
|
||||
|
||||
public Video getVideo(String id) throws JSONException {
|
||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet&id="+id+"&key="+key);
|
||||
//short data
|
||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id="+id+"&key="+key);
|
||||
String duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails").getString("duration");
|
||||
duration = formatDuration(duration);
|
||||
|
||||
res = res.getJSONArray("items").getJSONObject(0).getJSONObject("snippet");
|
||||
|
||||
res.put("videoID", id);
|
||||
res.put("duration", duration);
|
||||
res = fixThumbnail(res);
|
||||
|
||||
Video v = createVideofromJSON(res);
|
||||
v.setTitle(v.getTitle().replace("&", "&").replace(""", "\""));
|
||||
v.setAuthor(v.getAuthor().replace("&", "&").replace(""", "\""));
|
||||
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
@ -113,13 +159,25 @@ public class YoutubeAPIManager {
|
|||
Video video = null;
|
||||
try{
|
||||
String id = obj.getString("videoID");
|
||||
|
||||
String title = obj.getString("title");
|
||||
title = Html.fromHtml(title).toString();
|
||||
|
||||
String author = obj.getString("channelTitle");
|
||||
author = Html.fromHtml(author).toString();
|
||||
|
||||
String duration = obj.getString("duration");
|
||||
String thumb = obj.getString("thumb");
|
||||
|
||||
video = new Video(id, title, author, thumb);
|
||||
}catch(Exception ignored){}
|
||||
int downloadedAudio = dbManager.checkDownloaded(id, "mp3");
|
||||
int downloadedVideo = dbManager.checkDownloaded(id, "mp4");
|
||||
|
||||
int isPLaylist = 0;
|
||||
|
||||
video = new Video(id, title, author, duration, thumb, downloadedAudio, downloadedVideo, isPLaylist);
|
||||
}catch(Exception e){
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
return video;
|
||||
}
|
||||
|
||||
|
|
@ -187,5 +245,85 @@ public class YoutubeAPIManager {
|
|||
return o;
|
||||
}
|
||||
|
||||
public ArrayList<Video> getTrending(Context context) throws JSONException{
|
||||
String url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular®ionCode="+countryCODE+"&maxResults=25&key="+key;
|
||||
//short data
|
||||
JSONObject res = genericRequest(url);
|
||||
//extra data from the same videos
|
||||
JSONObject contentDetails = genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular®ionCode="+countryCODE+"&maxResults=25&key="+key);
|
||||
|
||||
JSONArray dataArray = res.getJSONArray("items");
|
||||
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
||||
for(int i = 0; i < dataArray.length(); i++){
|
||||
JSONObject element = dataArray.getJSONObject(i);
|
||||
JSONObject snippet = element.getJSONObject("snippet");
|
||||
|
||||
String duration = extraDataArray.getJSONObject(i).getJSONObject("contentDetails").getString("duration");
|
||||
duration = formatDuration(duration);
|
||||
|
||||
snippet.put("videoID", element.getString("id"));
|
||||
snippet.put("duration", duration);
|
||||
snippet = fixThumbnail(snippet);
|
||||
|
||||
Video v = createVideofromJSON(snippet);
|
||||
if(v.getThumb().isEmpty()){
|
||||
continue;
|
||||
}
|
||||
videos.add(v);
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
|
||||
public String formatDuration(String dur){
|
||||
String duration = "";
|
||||
String tmp = "";
|
||||
String hours = "";
|
||||
String minutes = "";
|
||||
String seconds = "";
|
||||
//removing P
|
||||
dur = dur.substring(1);
|
||||
for(int i = 0; i < dur.length(); i++){
|
||||
char c = dur.charAt(i);
|
||||
if(!Character.isDigit(c)){
|
||||
int nr = Character.getNumericValue(c);
|
||||
if(nr < 10){
|
||||
tmp = "0" + tmp;
|
||||
}
|
||||
if (c == 'D'){
|
||||
hours = String.valueOf(Integer.valueOf(tmp) * 24);
|
||||
}else if(c == 'T'){
|
||||
continue;
|
||||
}else if(c == 'H'){
|
||||
hours = tmp;
|
||||
}else if(c == 'M'){
|
||||
minutes = tmp;
|
||||
}else if(c == 'S'){
|
||||
seconds = tmp;
|
||||
}
|
||||
tmp = "";
|
||||
continue;
|
||||
}
|
||||
tmp = tmp + c;
|
||||
}
|
||||
|
||||
if(seconds.isEmpty()) seconds = "00";
|
||||
else if(Integer.parseInt(seconds) < 10) seconds = "0" + seconds;
|
||||
if(minutes.isEmpty()){
|
||||
if(hours.isEmpty()){
|
||||
minutes = "0";
|
||||
}else{
|
||||
minutes = "00";
|
||||
}
|
||||
}
|
||||
duration = minutes + ":" + seconds;
|
||||
|
||||
if(!hours.isEmpty()){
|
||||
duration = hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,21 +5,22 @@ import android.content.Context;
|
|||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DBManager extends SQLiteOpenHelper {
|
||||
|
||||
public static final String db_name = "ytdlnis";
|
||||
public static final String db_name = "ytdlnis_db";
|
||||
public static final int db_version = 1;
|
||||
public static final String results_table_name = "videoResults";
|
||||
public static final String history_table_name = "videoHistory";
|
||||
public static final String results_table_name = "results";
|
||||
public static final String history_table_name = "history";
|
||||
public static final String id = "id";
|
||||
public static final String videoId = "videoId";
|
||||
public static final String title = "title";
|
||||
public static final String author = "author";
|
||||
public static final String duration = "duration";
|
||||
public static final String thumb = "thumb";
|
||||
public static final String downloadedAudio = "downloadedAudio";
|
||||
public static final String downloadedVideo = "downloadedVideo";
|
||||
public static final String type = "type";
|
||||
public static final String time = "time";
|
||||
public static final String isPlaylistItem = "isPlaylistItem";
|
||||
|
|
@ -36,9 +37,10 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
+ videoId + " TEXT,"
|
||||
+ title + " TEXT,"
|
||||
+ author + " TEXT,"
|
||||
+ duration + " TEXT,"
|
||||
+ thumb + " TEXT,"
|
||||
+ type + " TEXT,"
|
||||
+ time + " TEXT,"
|
||||
+ downloadedAudio + " INTEGER,"
|
||||
+ downloadedVideo + " INTEGER,"
|
||||
+ isPlaylistItem + " INTENGER)";
|
||||
|
||||
sqLiteDatabase.execSQL(query);
|
||||
|
|
@ -48,62 +50,27 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
+ videoId + " TEXT,"
|
||||
+ title + " TEXT,"
|
||||
+ author + " TEXT,"
|
||||
+ duration + " TEXT,"
|
||||
+ thumb + " TEXT,"
|
||||
+ type + " TEXT,"
|
||||
+ time + " TEXT,"
|
||||
+ isPlaylistItem + " INTEGER)";
|
||||
|
||||
sqLiteDatabase.execSQL(query);
|
||||
}
|
||||
|
||||
public void recreateResultsTable(SQLiteDatabase sqLiteDatabase){
|
||||
String query = "CREATE TABLE " + results_table_name + " ("
|
||||
+ id + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ videoId + " TEXT,"
|
||||
+ title + " TEXT,"
|
||||
+ author + " TEXT,"
|
||||
+ thumb + " TEXT,"
|
||||
+ type + " TEXT,"
|
||||
+ time + " TEXT,"
|
||||
+ isPlaylistItem + " BOOLEAN)";
|
||||
+ isPlaylistItem + " INTENGER)";
|
||||
|
||||
sqLiteDatabase.execSQL(query);
|
||||
}
|
||||
|
||||
public void clearHistory(){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.execSQL("DROP TABLE IF EXISTS " + history_table_name);
|
||||
|
||||
String query = "CREATE TABLE " + history_table_name + " ("
|
||||
+ id + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ videoId + " TEXT,"
|
||||
+ title + " TEXT,"
|
||||
+ author + " TEXT,"
|
||||
+ thumb + " TEXT,"
|
||||
+ type + " TEXT,"
|
||||
+ time + " TEXT,"
|
||||
+ isPlaylistItem + " BOOLEAN)";
|
||||
|
||||
db.execSQL(query);
|
||||
db.execSQL("DELETE FROM " + history_table_name);
|
||||
|
||||
//remove downloaded statuses from results
|
||||
db.execSQL("UPDATE "+results_table_name+ " SET downloadedAudio=0, downloadedVideo=0" +
|
||||
" WHERE downloadedAudio=1 OR downloadedVideo=1");
|
||||
}
|
||||
|
||||
public void clearResults(){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.execSQL("DROP TABLE IF EXISTS " + results_table_name);
|
||||
|
||||
String query = "CREATE TABLE " + results_table_name + " ("
|
||||
+ id + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ videoId + " TEXT,"
|
||||
+ title + " TEXT,"
|
||||
+ author + " TEXT,"
|
||||
+ thumb + " TEXT,"
|
||||
+ type + " TEXT,"
|
||||
+ time + " TEXT,"
|
||||
+ isPlaylistItem + " BOOLEAN)";
|
||||
|
||||
db.execSQL(query);
|
||||
|
||||
db.execSQL("DELETE FROM " + results_table_name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -113,9 +80,33 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
onCreate(sqLiteDatabase);
|
||||
}
|
||||
|
||||
public ArrayList<Video> merrVideot(String table_name){
|
||||
|
||||
public ArrayList<Video> getResults(){
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + table_name, null);
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + results_table_name, null);
|
||||
ArrayList<Video> list = new ArrayList<>();
|
||||
|
||||
if(cursor.moveToFirst()){
|
||||
do {
|
||||
// on below line we are adding the data from cursor to our array list.
|
||||
list.add(new Video(cursor.getString(1),
|
||||
cursor.getString(2),
|
||||
cursor.getString(3),
|
||||
cursor.getString(4),
|
||||
cursor.getString(5),
|
||||
cursor.getInt(6),
|
||||
cursor.getInt(7),
|
||||
cursor.getInt(8)));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
public ArrayList<Video> getHistory(){
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name, null);
|
||||
ArrayList<Video> list = new ArrayList<>();
|
||||
|
||||
if(cursor.moveToFirst()){
|
||||
|
|
@ -127,7 +118,8 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
cursor.getString(4),
|
||||
cursor.getString(5),
|
||||
cursor.getString(6),
|
||||
cursor.getInt(7)));
|
||||
cursor.getString(7),
|
||||
cursor.getInt(8)));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
|
|
@ -135,57 +127,70 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
return list;
|
||||
}
|
||||
|
||||
public ArrayList<Video> merrRezultatet(){
|
||||
return merrVideot(results_table_name);
|
||||
}
|
||||
|
||||
public ArrayList<Video> merrHistorine(){
|
||||
return merrVideot(history_table_name);
|
||||
}
|
||||
|
||||
public Video shkoKohenRezultatit(String id, String downloadedTime){
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
ContentValues vlerat = new ContentValues();
|
||||
vlerat.put(time, downloadedTime);
|
||||
|
||||
db.update(results_table_name, vlerat, "videoId = ?", new String[]{id});
|
||||
return null;
|
||||
}
|
||||
|
||||
public void shtoVideoRezultat(ArrayList<Video> videot){
|
||||
public void addToResults(ArrayList<Video> videot){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
ContentValues vlerat = new ContentValues();
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
db.execSQL("DROP TABLE IF EXISTS " + results_table_name);
|
||||
recreateResultsTable(db);
|
||||
db.execSQL("DELETE FROM " + results_table_name);
|
||||
|
||||
for(Video v : videot){
|
||||
vlerat.put(videoId, v.getVideoId());
|
||||
vlerat.put(title, v.getTitle());
|
||||
vlerat.put(author, v.getAuthor());
|
||||
vlerat.put(thumb, v.getThumb());
|
||||
vlerat.put(type, v.getDownloadedType());
|
||||
vlerat.put(time, v.getDownloadedTime());
|
||||
vlerat.put(isPlaylistItem, v.getIsPlaylistItem());
|
||||
values.put(videoId, v.getVideoId());
|
||||
values.put(title, v.getTitle());
|
||||
values.put(author, v.getAuthor());
|
||||
values.put(duration, v.getDuration());
|
||||
values.put(thumb, v.getThumb());
|
||||
values.put(downloadedAudio, v.isAudioDownloaded());
|
||||
values.put(downloadedVideo, v.isVideoDownloaded());
|
||||
values.put(isPlaylistItem, v.getIsPlaylistItem());
|
||||
|
||||
db.insert(results_table_name, null, vlerat);
|
||||
db.insert(results_table_name, null, values);
|
||||
}
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
public void shtoVideoHistori(Video v){
|
||||
public void addToHistory(Video v){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
ContentValues vlerat = new ContentValues();
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
vlerat.put(videoId, v.getVideoId());
|
||||
vlerat.put(title, v.getTitle());
|
||||
vlerat.put(author, v.getAuthor());
|
||||
vlerat.put(thumb, v.getThumb());
|
||||
vlerat.put(type, v.getDownloadedType());
|
||||
vlerat.put(time, v.getDownloadedTime());
|
||||
values.put(videoId, v.getVideoId());
|
||||
values.put(title, v.getTitle());
|
||||
values.put(author, v.getAuthor());
|
||||
values.put(duration, v.getDuration());
|
||||
values.put(thumb, v.getThumb());
|
||||
values.put(type, v.getDownloadedType());
|
||||
values.put(time, v.getDownloadedTime());
|
||||
|
||||
db.insert(history_table_name, null, vlerat);
|
||||
db.insert(history_table_name, null, values);
|
||||
db.close();
|
||||
}
|
||||
|
||||
public void updateDownloadStatusOnResult(String id, String type){
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
switch (type){
|
||||
case "mp3":
|
||||
values.put(downloadedAudio, 1);
|
||||
break;
|
||||
case "mp4":
|
||||
values.put(downloadedVideo, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
db.update(results_table_name, values, "videoId = ?", new String[]{id});
|
||||
}
|
||||
|
||||
public int checkDownloaded(String id, String downloadType){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name + " WHERE videoId='" +
|
||||
id + "' AND type='"+downloadType + "' LIMIT 1", null);
|
||||
|
||||
if(cursor.moveToFirst()){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,30 +5,41 @@ public class Video {
|
|||
private String videoId;
|
||||
private String title;
|
||||
private String author;
|
||||
private String duration;
|
||||
private String thumb;
|
||||
private String downloadedType;
|
||||
private int downloadedAudio;
|
||||
private int downloadedVideo;
|
||||
private String downloadedTime;
|
||||
private int isPlaylistItem;
|
||||
|
||||
public Video(String videoId, String title, String author, String thumb, String downloadedType, String downloadedTime, int isPlaylistItem) {
|
||||
|
||||
// RESULTS OBJECT
|
||||
public Video(String videoId, String title, String author, String duration, String thumb,
|
||||
int downloadedAudio, int downloadedVideo, int isPlaylistItem) {
|
||||
this.videoId = videoId;
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.duration = duration;
|
||||
this.thumb = thumb;
|
||||
this.downloadedVideo = downloadedVideo;
|
||||
this.downloadedAudio = downloadedAudio;
|
||||
this.isPlaylistItem = isPlaylistItem;
|
||||
}
|
||||
|
||||
//HISTORY OBJECT
|
||||
public Video(String videoId, String title, String author, String duration, String thumb,
|
||||
String downloadedType, String downloadedTime, int isPlaylistItem) {
|
||||
this.videoId = videoId;
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.duration = duration;
|
||||
this.thumb = thumb;
|
||||
this.downloadedType = downloadedType;
|
||||
this.downloadedTime = downloadedTime;
|
||||
this.isPlaylistItem = isPlaylistItem;
|
||||
}
|
||||
|
||||
public Video(String videoId, String title, String author, String thumb) {
|
||||
this.videoId = videoId;
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
this.thumb = thumb;
|
||||
this.downloadedTime = null;
|
||||
this.downloadedType = null;
|
||||
this.isPlaylistItem = 0;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
|
|
@ -42,10 +53,6 @@ public class Video {
|
|||
return videoId;
|
||||
}
|
||||
|
||||
public void setVideoId(String videoId) {
|
||||
this.videoId = videoId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
|
@ -58,18 +65,14 @@ public class Video {
|
|||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
public String getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getThumb() {
|
||||
return thumb;
|
||||
}
|
||||
|
||||
public void setThumb(String thumb) {
|
||||
this.thumb = thumb;
|
||||
}
|
||||
|
||||
public String getDownloadedType() {
|
||||
return downloadedType;
|
||||
}
|
||||
|
|
@ -93,4 +96,12 @@ public class Video {
|
|||
public void setIsPlaylistItem(int playlistItem) {
|
||||
this.isPlaylistItem = playlistItem;
|
||||
}
|
||||
|
||||
public int isAudioDownloaded() {
|
||||
return downloadedAudio;
|
||||
}
|
||||
|
||||
public int isVideoDownloaded() {
|
||||
return downloadedVideo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import java.util.Map;
|
|||
* Helper class for providing sample content for user interfaces created by
|
||||
* Android template wizards.
|
||||
* <p>
|
||||
* TODO: Replace all uses of this class before publishing your app.
|
||||
*/
|
||||
public class PlaceholderContent {
|
||||
|
||||
|
|
|
|||
5
app/src/main/res/drawable/ic_go_up.xml
Normal file
5
app/src/main/res/drawable/ic_go_up.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#00A6FF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_music_downloaded.xml
Normal file
5
app/src/main/res/drawable/ic_music_downloaded.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#00A6FF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,7h-3v5.5c0,1.38 -1.12,2.5 -2.5,2.5S10,13.88 10,12.5s1.12,-2.5 2.5,-2.5c0.57,0 1.08,0.19 1.5,0.51L14,5h4v2zM4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6z"/>
|
||||
</vector>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@color/colorAccent" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
<path android:fillColor="?android:colorPrimary" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
</vector>
|
||||
|
|
|
|||
5
app/src/main/res/drawable/ic_video_downloaded.xml
Normal file
5
app/src/main/res/drawable/ic_video_downloaded.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#00A6FF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z"/>
|
||||
</vector>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#33DDFF" />
|
||||
<corners android:radius="10dp" />
|
||||
</shape>
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/frame_layout"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -14,13 +13,12 @@
|
|||
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"></FrameLayout>
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottomNavigationView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:itemActiveIndicatorStyle="@style/BottomNavDefault"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
|
|
|
|||
76
app/src/main/res/layout/download_all_card.xml
Normal file
76
app/src/main/res/layout/download_all_card.xml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/downloadall_relative_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/downloadall_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardBackgroundColor="#111"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="10dp"
|
||||
app:cardMaxElevation="12dp"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:layout_margin="10dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/downloadall_image_view"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/downloadall_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/download_all"
|
||||
android:gravity="left|center"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="2"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/downloadall_button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom|right"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/downloadall_music"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:icon="@drawable/ic_music"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/downloadall_video"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:icon="@drawable/ic_video"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,28 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swiperefresh"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/historycoordinator"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
>
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/history_big_linearlayout">
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical" />
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
</ScrollView>
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/history_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/history"
|
||||
app:menu="@menu/history_menu"
|
||||
/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swiperefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/history_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<include layout="@layout/history_card"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/shimmer_history_framelayout"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/shimmer_history_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_history"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@drawable/ic_go_up"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -1,23 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
>
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/homecoordinator"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical" >
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/home_big_linearlayout"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/home_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/app_name"
|
||||
app:menu="@menu/main_menu"
|
||||
/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swiperefreshhome"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<ScrollView
|
||||
android:visibility="visible"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<include layout="@layout/download_all_card"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include layout="@layout/result_card"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/shimmer_results_framelayout"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shimmer_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_home"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:visibility="invisible"
|
||||
app:srcCompat="@drawable/ic_go_up"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
26
app/src/main/res/layout/fragment_settings.xml
Normal file
26
app/src/main/res/layout/fragment_settings.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/preference_coordinatorLayout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/home_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/settings"
|
||||
app:menu="@menu/empty_menu"
|
||||
/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
110
app/src/main/res/layout/history_card.xml
Normal file
110
app/src/main/res/layout/history_card.xml
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/history_card_constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/history_relative_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="H,2.66:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/history_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="10dp"
|
||||
app:cardMaxElevation="12dp"
|
||||
app:cardBackgroundColor="@color/black"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:layout_margin="10dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/history_image_view"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_title"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="100dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="2"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_info_bottom"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_info_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom|right"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/history_download_button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|right"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/history_download_button_type"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
119
app/src/main/res/layout/history_card_shimmer.xml
Normal file
119
app/src/main/res/layout/history_card_shimmer.xml
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout android:id="@+id/history_shimmer_relative_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
app:layout_constraintDimensionRatio="H,2.66:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/history_shimmer_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="10dp"
|
||||
app:cardMaxElevation="12dp"
|
||||
app:cardBackgroundColor="@color/black"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:layout_margin="10dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/history_shimmer_image_view"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_shimmer_title"
|
||||
android:layout_width="250dp"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_shimmer_title2"
|
||||
android:layout_width="150dp"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_shimmer_bottom"
|
||||
android:layout_width="100dp"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="bottom"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_shimmer_date"
|
||||
android:layout_width="100dp"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:gravity="bottom"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/history_shimmer_info_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom|right"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/history_shimmer_download_button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|right"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/history_shimmer_download_button_type"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
115
app/src/main/res/layout/result_card.xml
Normal file
115
app/src/main/res/layout/result_card.xml
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/result_card_constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/result_relative_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="H,16:9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/result_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="10dp"
|
||||
app:cardMaxElevation="12dp"
|
||||
app:cardBackgroundColor="@color/black"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:layout_margin="10dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/result_image_view"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="2"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_info_bottom"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/download_button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom|right"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/download_music"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:icon="@drawable/ic_music"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/download_video"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:icon="@drawable/ic_video"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/download_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="7dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:backgroundTintMode="src_in"
|
||||
android:progress="0"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:scaleY="2" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
137
app/src/main/res/layout/result_card_shimmer.xml
Normal file
137
app/src/main/res/layout/result_card_shimmer.xml
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout android:id="@+id/result_shimmer_relative_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="230dp"
|
||||
app:layout_constraintDimensionRatio="H,16:9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/result_shimmer_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="10dp"
|
||||
app:cardMaxElevation="12dp"
|
||||
app:cardBackgroundColor="@color/grey"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:layout_margin="10dp"
|
||||
>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/result_shimmer_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_shimmer_title"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="15sp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@color/light_grey"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="2"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="15sp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@color/light_grey"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="2"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:background="@color/light_grey"
|
||||
android:gravity="bottom"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="15sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginLeft="65dp"
|
||||
android:background="@color/light_grey"
|
||||
android:gravity="bottom"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="20dp"
|
||||
android:textSize="15sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:shadowRadius="1.5"
|
||||
android:shadowDx="4"
|
||||
android:shadowDy="4"
|
||||
android:shadowColor="@color/black" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/download_button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom|right"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/download_music"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:backgroundTint="@color/light_grey"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
|
||||
android:id="@+id/download_video"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:backgroundTint="@color/light_grey"
|
||||
android:layout_marginLeft="10dp"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" android:theme="@style/AppTheme">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" android:theme="@style/AppDefaultTheme">
|
||||
|
||||
<item android:title="Kryesore"
|
||||
<item android:title="@string/home"
|
||||
android:icon="@drawable/ic_home"
|
||||
android:id="@+id/home"/>
|
||||
<item android:title="Historia"
|
||||
<item android:title="@string/history"
|
||||
android:icon="@drawable/ic_history"
|
||||
android:id="@+id/history"/>
|
||||
<item android:title="Cilësime"
|
||||
<item android:title="@string/settings"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:id="@+id/settings"/>
|
||||
</menu>
|
||||
6
app/src/main/res/menu/empty_menu.xml
Normal file
6
app/src/main/res/menu/empty_menu.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity" >
|
||||
</menu>
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
android:id="@+id/search"
|
||||
android:title="@string/search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:showAsAction="ifRoom|collapseActionView"
|
||||
app:showAsAction="collapseActionView|always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
||||
|
||||
<item
|
||||
|
|
|
|||
29
app/src/main/res/values-sq-rAL/strings.xml
Normal file
29
app/src/main/res/values-sq-rAL/strings.xml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="Trending"><b>Në Trend</b></string>
|
||||
<string name="search">Kërko</string>
|
||||
<string name="fshi_rezultatet">Pastro Rezultatet</string>
|
||||
<string name="fshi_historin">Pastro Historinë</string>
|
||||
<string name="end_of_results"><b>Arrite fundin e rezultateve</b></string>
|
||||
<string name="download_all">Shkarko të gjitha</string>
|
||||
<string name="history">Historia</string>
|
||||
<string name="settings">Cilësime</string>
|
||||
<string name="home">Kryesore</string>
|
||||
<string name="directories">Direktoritë</string>
|
||||
<string name="music_directory">Direktoria e Muzikës</string>
|
||||
<string name="video_directory">Direktoria e Videos</string>
|
||||
<string name="updating">Përditësim</string>
|
||||
<string name="ytdl_update_hint">Kliko këtë nëse ke probleme me shkarkimin</string>
|
||||
<string name="update_ytdl">Përditëso YTDL</string>
|
||||
<string name="about">Rreth</string>
|
||||
<string name="source_code">Kodi</string>
|
||||
<string name="search_hint">Kërko nga YouTube</string>
|
||||
<string name="download_already_started">Nuk mund te filloj! Nje shkarkim tjeter është në punë!</string>
|
||||
<string name="try_again_after_permission">Provo përsëri pasi ke pranuar lejen!</string>
|
||||
<string name="download_success">U shkarkua!</string>
|
||||
<string name="failed_download">Dështim në shkarkim! :(</string>
|
||||
<string name="download_start">Shkarkimi Filloi!</string>
|
||||
<string name="download_notification_channel_name">Shkarkimet e Skedarëve</string>
|
||||
<string name="download_notification_channel_description">Njoftimi i cili tregon progresin e shkarkimeve te skedarëve audio dhe video</string>
|
||||
<string name="failed_making_directory">Dështim në krijimin e Direktorisë së re! :(</string>
|
||||
</resources>
|
||||
|
|
@ -1,12 +1,4 @@
|
|||
<resources>
|
||||
<!-- Reply Preference -->
|
||||
<string-array name="reply_entries">
|
||||
<item>Reply</item>
|
||||
<item>Reply to all</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="reply_values">
|
||||
<item>reply</item>
|
||||
<item>reply_all</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#228DFF</color>
|
||||
<color name="colorPrimaryDark">#1B6CC3</color>
|
||||
<color name="colorAccent">#03A9F4</color>
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="black">#000000</color>
|
||||
<color name="grey">#323232</color>
|
||||
<color name="light_grey">#A6A6A6</color>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="text_margin">16dp</dimen>
|
||||
</resources>
|
||||
<resources></resources>
|
||||
|
|
@ -1,45 +1,29 @@
|
|||
<resources>
|
||||
<string name="app_name">YTDLnis</string>
|
||||
<string name="btn_streaming_example">Streaming Example</string>
|
||||
<string name="btn_downloading_example">Downloading Example</string>
|
||||
<string name="btn_command_example">Custom Command Example</string>
|
||||
<string name="btn_update">Update yt-dlp</string>
|
||||
<string name="btn_start_stream">Start Stream</string>
|
||||
<string name="btn_start_download">Start Download</string>
|
||||
<string name="url_hint">enter url here</string>
|
||||
<string name="btn_run_command">Run Command</string>
|
||||
<string name="command_hint">enter command here</string>
|
||||
<string name="url_error">enter a valid url</string>
|
||||
<string name="command_error">enter a valid command</string>
|
||||
<string name="start">Do the thing</string>
|
||||
<string name="example_command">Example command: --extract-audio --audio-format mp3 -o /sdcard/Download/youtubedl-android/%(title)s.%(ext)s https://www.youtube.com/watch?v=dQw4w9WgXcQ</string>
|
||||
<string name="download_start">Starting Download</string>
|
||||
<string name="download_complete">Download Complete</string>
|
||||
<string name="download_failed">Download Failed</string>
|
||||
<string name="command_start">Starting Command</string>
|
||||
<string name="command_complete">Command Complete</string>
|
||||
<string name="command_failed">Command Failed</string>
|
||||
<string name="use_config_file">Use Config File?</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
<string name="app_name" translatable="false">YTDLnis</string>
|
||||
<string name="search">Search</string>
|
||||
<!-- Preference Titles -->
|
||||
<string name="messages_header">Messages</string>
|
||||
<string name="sync_header">Sync</string>
|
||||
|
||||
<!-- Messages Preferences -->
|
||||
<string name="signature_title">Your signature</string>
|
||||
<string name="reply_title">Default reply action</string>
|
||||
|
||||
<!-- Sync Preferences -->
|
||||
<string name="sync_title">Sync email periodically</string>
|
||||
<string name="attachment_title">Download incoming attachments</string>
|
||||
<string name="attachment_summary_on">Automatically download attachments for incoming emails
|
||||
</string>
|
||||
<string name="attachment_summary_off">Only download attachments when manually requested</string>
|
||||
<string name="shkarko">Shkarko</string>
|
||||
<string name="shkruaj_url_e_youtube">Shkruaj URL e YouTube</string>
|
||||
<string name="download_path">sdcard/Download</string>
|
||||
<string name="fshi_historin">Fshi Historinë</string>
|
||||
<string name="fshi_rezultatet">Fshi rezultatet</string>
|
||||
<string name="fshi_historin">Clear history</string>
|
||||
<string name="fshi_rezultatet">Clear Results</string>
|
||||
<string name="Trending"><b>Trending</b></string>
|
||||
<string name="end_of_results"><b>You reached the end of results</b></string>
|
||||
<string name="download_all">Download All</string>
|
||||
<string name="history">History</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="directories">Directories</string>
|
||||
<string name="music_directory">Music Directory</string>
|
||||
<string name="video_directory">Video Directory</string>
|
||||
<string name="updating">Updating</string>
|
||||
<string name="ytdl_update_hint">Click this if you face download issues</string>
|
||||
<string name="update_ytdl">Update YTDL</string>
|
||||
<string name="about">About</string>
|
||||
<string name="source_code">Source Code</string>
|
||||
<string name="search_hint">Search from YouTube</string>
|
||||
<string name="download_already_started">Can\'t Start Download! Another one is not done yet!</string>
|
||||
<string name="try_again_after_permission">Try again after accepting the permission!</string>
|
||||
<string name="download_success">Download Complete!</string>
|
||||
<string name="failed_download">Failed Downloading! :(</string>
|
||||
<string name="download_start">Download Started!</string>
|
||||
<string name="download_notification_channel_name">File Downloads</string>
|
||||
<string name="download_notification_channel_description">Notification that shows the download progress of audio and video files</string>
|
||||
<string name="failed_making_directory">Failed to create the new Directory! :(</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,40 +1,10 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.Material3.DayNight">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/material_dynamic_primary60</item>
|
||||
<item name="colorPrimaryDark">@color/material_dynamic_primary20</item>
|
||||
<item name="colorAccent">@color/material_dynamic_primary40</item>
|
||||
<item name="background">@color/white</item>
|
||||
</style>
|
||||
|
||||
<!-- Base application theme less than android 12 -->
|
||||
<style name="AppDefaultTheme" parent="Theme.Material3.DayNight">
|
||||
<style name="AppDefaultTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">#228DFF</item>
|
||||
<item name="colorPrimaryDark">#1B6CC3</item>
|
||||
<item name="colorAccent">#03A9F4</item>
|
||||
<item name="android:statusBarColor">#000000</item>
|
||||
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:color">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light" />
|
||||
|
||||
<style name="RoundedBg" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">50%</item>
|
||||
</style>
|
||||
|
||||
<style name="BottomNav" parent="Widget.Material3.BottomNavigationView.ActiveIndicator">
|
||||
<item name="android:color">@color/material_dynamic_primary50</item>
|
||||
</style>
|
||||
|
||||
<style name="BottomNavDefault" parent="Widget.Material3.BottomNavigationView.ActiveIndicator">
|
||||
<item name="android:color">#228DFF</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,40 +1,41 @@
|
|||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
>
|
||||
|
||||
<PreferenceCategory app:title="Skedaret">
|
||||
<Preference
|
||||
android:layout="@layout/fragment_settings"/>
|
||||
|
||||
<PreferenceCategory app:title="@string/directories">
|
||||
<Preference
|
||||
app:key="music_path"
|
||||
app:title="Direktoria e Muzikës"></Preference>
|
||||
|
||||
app:title="@string/music_directory" />
|
||||
<Preference
|
||||
app:key="video_path"
|
||||
app:title="Direktoria e Videos"></Preference>
|
||||
|
||||
|
||||
app:title="@string/video_directory" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="Perditesim">
|
||||
|
||||
<PreferenceCategory app:title="@string/updating">
|
||||
<Preference
|
||||
app:key="update"
|
||||
app:summary="Provo kete cilesim nese paraqitesh me probleme ne shkarkim"
|
||||
app:title="Perditeso Komponentin YoutubeDL"></Preference>
|
||||
|
||||
app:summary="@string/ytdl_update_hint"
|
||||
app:title="@string/update_ytdl" />
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
<PreferenceCategory app:title="Rreth">
|
||||
<PreferenceCategory app:title="@string/about">
|
||||
|
||||
<Preference
|
||||
app:key="rreth"
|
||||
app:summary="https://github.com/deniscerri/ytdlnis"
|
||||
app:title="Kodi">
|
||||
app:title="@string/source_code">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:data="https://github.com/deniscerri/ytdlnis"
|
||||
/>
|
||||
/>
|
||||
</Preference>
|
||||
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
@ -16,8 +16,8 @@ buildscript {
|
|||
}
|
||||
|
||||
def versionMajor = 1
|
||||
def versionMinor = 0
|
||||
def versionPatch = 1
|
||||
def versionMinor = 1
|
||||
def versionPatch = 0
|
||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||
|
||||
ext {
|
||||
|
|
@ -43,6 +43,7 @@ allprojects {
|
|||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
1
common/.gitignore
vendored
1
common/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode project.versionCode
|
||||
versionName project.versionName
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
testImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
||||
|
||||
implementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVer"
|
||||
implementation group: 'org.apache.commons', name: 'commons-compress', version: "$commonsCompressVer"
|
||||
}
|
||||
21
common/proguard-rules.pro
vendored
21
common/proguard-rules.pro
vendored
|
|
@ -1,21 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package com.yausername.youtubedl_common;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.yausername.youtubedl_common.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.yausername.youtubedl_common">
|
||||
|
||||
/
|
||||
</manifest>
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.yausername.youtubedl_common;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class SharedPrefsHelper {
|
||||
|
||||
private SharedPrefsHelper() {
|
||||
}
|
||||
|
||||
private static final String sharedPrefsName = "youtubedl-android";
|
||||
|
||||
public static void update(Context appContext, String key, String value) {
|
||||
SharedPreferences pref = appContext.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = pref.edit();
|
||||
editor.putString(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String get(Context appContext, String key) {
|
||||
SharedPreferences pref = appContext.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE);
|
||||
return pref.getString(key, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
package com.yausername.youtubedl_common.utils;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Enumeration;
|
||||
|
||||
|
||||
public class ZipUtils {
|
||||
|
||||
private ZipUtils() {
|
||||
}
|
||||
|
||||
public static void unzip(File sourceFile, File targetDirectory) throws IOException, ErrnoException, IllegalAccessException {
|
||||
try (ZipFile zipFile = new ZipFile(sourceFile)) {
|
||||
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipArchiveEntry entry = entries.nextElement();
|
||||
File entryDestination = new File(targetDirectory, entry.getName());
|
||||
// prevent zipSlip
|
||||
if (!entryDestination.getCanonicalPath().startsWith(targetDirectory.getCanonicalPath() + File.separator)) {
|
||||
throw new IllegalAccessException("Entry is outside of the target dir: " + entry.getName());
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
entryDestination.mkdirs();
|
||||
} else if (entry.isUnixSymlink()) {
|
||||
try (InputStream in = zipFile.getInputStream(entry)) {
|
||||
String symlink = IOUtils.toString(in, StandardCharsets.UTF_8);
|
||||
Os.symlink(symlink, entryDestination.getAbsolutePath());
|
||||
}
|
||||
} else {
|
||||
entryDestination.getParentFile().mkdirs();
|
||||
try (InputStream in = zipFile.getInputStream(entry);
|
||||
OutputStream out = new FileOutputStream(entryDestination)) {
|
||||
IOUtils.copy(in, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void unzip(InputStream inputStream, File targetDirectory) throws IOException, IllegalAccessException {
|
||||
try (ZipArchiveInputStream zis = new ZipArchiveInputStream(new BufferedInputStream(inputStream))) {
|
||||
ZipArchiveEntry entry = null;
|
||||
while ((entry = zis.getNextZipEntry()) != null) {
|
||||
File entryDestination = new File(targetDirectory, entry.getName());
|
||||
// prevent zipSlip
|
||||
if (!entryDestination.getCanonicalPath().startsWith(targetDirectory.getCanonicalPath() + File.separator)) {
|
||||
throw new IllegalAccessException("Entry is outside of the target dir: " + entry.getName());
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
entryDestination.mkdirs();
|
||||
} else {
|
||||
entryDestination.getParentFile().mkdirs();
|
||||
try (OutputStream out = new FileOutputStream(entryDestination)) {
|
||||
IOUtils.copy(zis, out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.yausername.youtubedl_common;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
1
ffmpeg/.gitignore
vendored
1
ffmpeg/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode project.versionCode
|
||||
versionName project.versionName
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation project(':common')
|
||||
compileOnly project(':library')
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
testImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
||||
|
||||
implementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVer"
|
||||
}
|
||||
21
ffmpeg/proguard-rules.pro
vendored
21
ffmpeg/proguard-rules.pro
vendored
|
|
@ -1,21 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package com.yausername.ffmpeg;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.yausername.ffmpeg.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.yausername.ffmpeg" />
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package com.yausername.ffmpeg;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.yausername.youtubedl_android.YoutubeDLException;
|
||||
import com.yausername.youtubedl_common.SharedPrefsHelper;
|
||||
import com.yausername.youtubedl_common.utils.ZipUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FFmpeg {
|
||||
|
||||
private static final FFmpeg INSTANCE = new FFmpeg();
|
||||
protected static final String baseName = "youtubedl-android";
|
||||
private static final String packagesRoot = "packages";
|
||||
private static final String ffmegDirName = "ffmpeg";
|
||||
private static final String ffmpegLibName = "libffmpeg.zip.so";
|
||||
private static final String ffmpegLibVersion = "ffmpegLibVersion";
|
||||
|
||||
private boolean initialized = false;
|
||||
private File binDir;
|
||||
|
||||
private FFmpeg(){
|
||||
}
|
||||
|
||||
public static FFmpeg getInstance(){
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
synchronized public void init(Context appContext) throws YoutubeDLException {
|
||||
if (initialized) return;
|
||||
|
||||
File baseDir = new File(appContext.getNoBackupFilesDir(), baseName);
|
||||
if(!baseDir.exists()) baseDir.mkdir();
|
||||
|
||||
binDir = new File(appContext.getApplicationInfo().nativeLibraryDir);
|
||||
|
||||
File packagesDir = new File(baseDir, packagesRoot);
|
||||
File ffmpegDir = new File(packagesDir, ffmegDirName);
|
||||
initFFmpeg(appContext, ffmpegDir);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void initFFmpeg(Context appContext, File ffmpegDir) throws YoutubeDLException {
|
||||
File ffmpegLib = new File(binDir, ffmpegLibName);
|
||||
// using size of lib as version
|
||||
String ffmpegSize = String.valueOf(ffmpegLib.length());
|
||||
if (!ffmpegDir.exists() || shouldUpdateFFmpeg(appContext, ffmpegSize)) {
|
||||
FileUtils.deleteQuietly(ffmpegDir);
|
||||
ffmpegDir.mkdirs();
|
||||
try {
|
||||
ZipUtils.unzip(ffmpegLib, ffmpegDir);
|
||||
} catch (Exception e) {
|
||||
FileUtils.deleteQuietly(ffmpegDir);
|
||||
throw new YoutubeDLException("failed to initialize", e);
|
||||
}
|
||||
updateFFmpeg(appContext, ffmpegSize);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpdateFFmpeg(@NonNull Context appContext, @NonNull String version) {
|
||||
return !version.equals(SharedPrefsHelper.get(appContext, ffmpegLibVersion));
|
||||
}
|
||||
|
||||
private void updateFFmpeg(@NonNull Context appContext, @NonNull String version) {
|
||||
SharedPrefsHelper.update(appContext, ffmpegLibVersion, version);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
<resources>
|
||||
<string name="app_name">ffmpeg</string>
|
||||
</resources>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.yausername.ffmpeg;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
1
library/.gitignore
vendored
1
library/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode project.versionCode
|
||||
versionName project.versionName
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation project(':common')
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appCompatVer"
|
||||
testImplementation "junit:junit:$junitVer"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
||||
|
||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "$jacksonVer"
|
||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: "$jacksonVer"
|
||||
implementation group: 'commons-io', name: 'commons-io', version: "$commonsIoVer"
|
||||
}
|
||||
21
library/proguard-rules.pro
vendored
21
library/proguard-rules.pro
vendored
|
|
@ -1,21 +0,0 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.yausername.youtubedl_android.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.yausername.youtubedl_android" />
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
public interface DownloadProgressCallback {
|
||||
void onProgressUpdate(float progress, long etaInSeconds, String line);
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
class StreamGobbler extends Thread {
|
||||
|
||||
private final InputStream stream;
|
||||
private final StringBuffer buffer;
|
||||
|
||||
private static final String TAG = "StreamGobbler";
|
||||
|
||||
public StreamGobbler(StringBuffer buffer, InputStream stream) {
|
||||
this.stream = stream;
|
||||
this.buffer = buffer;
|
||||
start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
|
||||
int nextChar;
|
||||
while ((nextChar = in.read()) != -1) {
|
||||
this.buffer.append((char) nextChar);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.e(TAG, "failed to read stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
class StreamProcessExtractor extends Thread {
|
||||
private static final int GROUP_PERCENT = 1;
|
||||
private static final int GROUP_MINUTES = 2;
|
||||
private static final int GROUP_SECONDS = 3;
|
||||
private final InputStream stream;
|
||||
private final StringBuffer buffer;
|
||||
private final DownloadProgressCallback callback;
|
||||
|
||||
private final Pattern p = Pattern.compile("\\[download\\]\\s+(\\d+\\.\\d)% .* ETA (\\d+):(\\d+)");
|
||||
|
||||
private static final String TAG = "StreamProcessExtractor";
|
||||
|
||||
public StreamProcessExtractor(StringBuffer buffer, InputStream stream, DownloadProgressCallback callback) {
|
||||
this.stream = stream;
|
||||
this.buffer = buffer;
|
||||
this.callback = callback;
|
||||
this.start();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
|
||||
StringBuilder currentLine = new StringBuilder();
|
||||
int nextChar;
|
||||
while ((nextChar = in.read()) != -1) {
|
||||
buffer.append((char) nextChar);
|
||||
if (nextChar == '\r' && callback != null) {
|
||||
processOutputLine(currentLine.toString());
|
||||
currentLine.setLength(0);
|
||||
continue;
|
||||
}
|
||||
currentLine.append((char) nextChar);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (BuildConfig.DEBUG)
|
||||
Log.e(TAG, "failed to read stream", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processOutputLine(String line) {
|
||||
Matcher m = p.matcher(line);
|
||||
if (m.matches()) {
|
||||
float progress = Float.parseFloat(m.group(GROUP_PERCENT));
|
||||
long eta = convertToSeconds(m.group(GROUP_MINUTES), m.group(GROUP_SECONDS));
|
||||
callback.onProgressUpdate(progress, eta, line);
|
||||
}
|
||||
}
|
||||
|
||||
private int convertToSeconds(String minutes, String seconds) {
|
||||
return Integer.parseInt(minutes) * 60 + Integer.parseInt(seconds);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yausername.youtubedl_android.mapper.VideoInfo;
|
||||
import com.yausername.youtubedl_common.SharedPrefsHelper;
|
||||
import com.yausername.youtubedl_common.utils.ZipUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class YoutubeDL {
|
||||
|
||||
private static final YoutubeDL INSTANCE = new YoutubeDL();
|
||||
protected static final String baseName = "youtubedl-android";
|
||||
private static final String packagesRoot = "packages";
|
||||
private static final String pythonBinName = "libpython.bin.so";
|
||||
private static final String pythonLibName = "libpython.zip.so";
|
||||
private static final String pythonDirName = "python";
|
||||
private static final String ffmpegDirName = "ffmpeg";
|
||||
protected static final String youtubeDLDirName = "yt-dlp";
|
||||
private static final String youtubeDLBin = "__main__.py";
|
||||
protected static final String youtubeDLFile = "yt_dlp.zip";
|
||||
private static final String pythonLibVersion = "pythonLibVersion";
|
||||
|
||||
private boolean initialized = false;
|
||||
private File pythonPath;
|
||||
private File youtubeDLPath;
|
||||
private File binDir;
|
||||
|
||||
private String ENV_LD_LIBRARY_PATH;
|
||||
private String ENV_SSL_CERT_FILE;
|
||||
private String ENV_PYTHONHOME;
|
||||
|
||||
protected static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private YoutubeDL(){
|
||||
}
|
||||
|
||||
public static YoutubeDL getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
synchronized public void init(Context appContext) throws YoutubeDLException {
|
||||
if (initialized) return;
|
||||
|
||||
File baseDir = new File(appContext.getNoBackupFilesDir(), baseName);
|
||||
if(!baseDir.exists()) baseDir.mkdir();
|
||||
|
||||
File packagesDir = new File(baseDir, packagesRoot);
|
||||
binDir = new File(appContext.getApplicationInfo().nativeLibraryDir);
|
||||
pythonPath = new File(binDir, pythonBinName);
|
||||
File pythonDir = new File(packagesDir, pythonDirName);
|
||||
File ffmpegDir = new File(packagesDir, ffmpegDirName);
|
||||
|
||||
File youtubeDLDir = new File(baseDir, youtubeDLDirName);
|
||||
youtubeDLPath = new File(youtubeDLDir, youtubeDLBin);
|
||||
|
||||
ENV_LD_LIBRARY_PATH = pythonDir.getAbsolutePath() + "/usr/lib" + ":" + ffmpegDir.getAbsolutePath() + "/usr/lib";
|
||||
ENV_SSL_CERT_FILE = pythonDir.getAbsolutePath() + "/usr/etc/tls/cert.pem";
|
||||
ENV_PYTHONHOME = pythonDir.getAbsolutePath() + "/usr";
|
||||
|
||||
initPython(appContext, pythonDir);
|
||||
initYoutubeDL(appContext, youtubeDLDir);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
protected void initYoutubeDL(Context appContext, File youtubeDLDir) throws YoutubeDLException {
|
||||
if (!youtubeDLDir.exists()) {
|
||||
youtubeDLDir.mkdirs();
|
||||
try {
|
||||
ZipUtils.unzip(appContext.getResources().openRawResource(R.raw.yt_dlp), youtubeDLDir);
|
||||
} catch (Exception e) {
|
||||
FileUtils.deleteQuietly(youtubeDLDir);
|
||||
throw new YoutubeDLException("failed to initialize", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initPython(Context appContext, File pythonDir) throws YoutubeDLException {
|
||||
File pythonLib = new File(binDir, pythonLibName);
|
||||
// using size of lib as version
|
||||
String pythonSize = String.valueOf(pythonLib.length());
|
||||
if (!pythonDir.exists() || shouldUpdatePython(appContext, pythonSize)) {
|
||||
FileUtils.deleteQuietly(pythonDir);
|
||||
pythonDir.mkdirs();
|
||||
try {
|
||||
ZipUtils.unzip(pythonLib, pythonDir);
|
||||
} catch (Exception e) {
|
||||
FileUtils.deleteQuietly(pythonDir);
|
||||
throw new YoutubeDLException("failed to initialize", e);
|
||||
}
|
||||
updatePython(appContext, pythonSize);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldUpdatePython(@NonNull Context appContext, @NonNull String version) {
|
||||
return !version.equals(SharedPrefsHelper.get(appContext, pythonLibVersion));
|
||||
}
|
||||
|
||||
private void updatePython(@NonNull Context appContext, @NonNull String version) {
|
||||
SharedPrefsHelper.update(appContext, pythonLibVersion, version);
|
||||
}
|
||||
|
||||
private void assertInit() {
|
||||
if (!initialized) throw new IllegalStateException("instance not initialized");
|
||||
}
|
||||
|
||||
public VideoInfo getInfo(String url) throws YoutubeDLException, InterruptedException {
|
||||
YoutubeDLRequest request = new YoutubeDLRequest(url);
|
||||
return getInfo(request);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public VideoInfo getInfo(YoutubeDLRequest request) throws YoutubeDLException, InterruptedException {
|
||||
request.addOption("--dump-json");
|
||||
YoutubeDLResponse response = execute(request, null);
|
||||
|
||||
VideoInfo videoInfo;
|
||||
try {
|
||||
videoInfo = objectMapper.readValue(response.getOut(), VideoInfo.class);
|
||||
} catch (IOException e) {
|
||||
throw new YoutubeDLException("Unable to parse video information", e);
|
||||
}
|
||||
|
||||
if(videoInfo == null){
|
||||
throw new YoutubeDLException("Failed to fetch video information");
|
||||
}
|
||||
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
public YoutubeDLResponse execute(YoutubeDLRequest request) throws YoutubeDLException, InterruptedException {
|
||||
return execute(request, null);
|
||||
}
|
||||
|
||||
public YoutubeDLResponse execute(YoutubeDLRequest request, @Nullable DownloadProgressCallback callback) throws YoutubeDLException, InterruptedException {
|
||||
assertInit();
|
||||
|
||||
// disable caching unless explicitly requested
|
||||
if(request.getOption("--cache-dir") == null){
|
||||
request.addOption("--no-cache-dir");
|
||||
}
|
||||
|
||||
YoutubeDLResponse youtubeDLResponse;
|
||||
Process process;
|
||||
int exitCode;
|
||||
StringBuffer outBuffer = new StringBuffer(); //stdout
|
||||
StringBuffer errBuffer = new StringBuffer(); //stderr
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
List<String> args = request.buildCommand();
|
||||
List<String> command = new ArrayList<>();
|
||||
command.addAll(Arrays.asList(pythonPath.getAbsolutePath(), youtubeDLPath.getAbsolutePath()));
|
||||
command.addAll(args);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
Map<String, String> env = processBuilder.environment();
|
||||
env.put("LD_LIBRARY_PATH", ENV_LD_LIBRARY_PATH);
|
||||
env.put("SSL_CERT_FILE", ENV_SSL_CERT_FILE);
|
||||
env.put("PATH", System.getenv("PATH") + ":" + binDir.getAbsolutePath());
|
||||
env.put("PYTHONHOME", ENV_PYTHONHOME);
|
||||
|
||||
try {
|
||||
process = processBuilder.start();
|
||||
} catch (IOException e) {
|
||||
throw new YoutubeDLException(e);
|
||||
}
|
||||
|
||||
InputStream outStream = process.getInputStream();
|
||||
InputStream errStream = process.getErrorStream();
|
||||
|
||||
StreamProcessExtractor stdOutProcessor = new StreamProcessExtractor(outBuffer, outStream, callback);
|
||||
StreamGobbler stdErrProcessor = new StreamGobbler(errBuffer, errStream);
|
||||
|
||||
try {
|
||||
stdOutProcessor.join();
|
||||
stdErrProcessor.join();
|
||||
exitCode = process.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
process.destroy();
|
||||
throw e;
|
||||
}
|
||||
|
||||
String out = outBuffer.toString();
|
||||
String err = errBuffer.toString();
|
||||
|
||||
if (exitCode > 0) {
|
||||
throw new YoutubeDLException(err);
|
||||
}
|
||||
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
|
||||
youtubeDLResponse = new YoutubeDLResponse(command, exitCode, elapsedTime, out, err);
|
||||
|
||||
return youtubeDLResponse;
|
||||
}
|
||||
|
||||
synchronized public UpdateStatus updateYoutubeDL(Context appContext) throws YoutubeDLException {
|
||||
assertInit();
|
||||
try {
|
||||
return YoutubeDLUpdater.update(appContext);
|
||||
} catch (IOException e) {
|
||||
throw new YoutubeDLException("failed to update youtube-dl", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String version(Context appContext) {
|
||||
return YoutubeDLUpdater.version(appContext);
|
||||
}
|
||||
|
||||
public enum UpdateStatus {
|
||||
DONE, ALREADY_UP_TO_DATE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
public class YoutubeDLException extends Exception {
|
||||
|
||||
public YoutubeDLException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public YoutubeDLException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
public YoutubeDLException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class YoutubeDLOptions {
|
||||
|
||||
private Map<String, String> options = new LinkedHashMap<>();
|
||||
|
||||
public YoutubeDLOptions addOption(@NonNull String key, @NonNull String value){
|
||||
options.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public YoutubeDLOptions addOption(@NonNull String key, @NonNull Number value){
|
||||
options.put(key, value.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public YoutubeDLOptions addOption(String key){
|
||||
options.put(key, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getOption(String key){
|
||||
return options.get(key);
|
||||
}
|
||||
|
||||
public List<String> buildOptions(){
|
||||
List<String> optionsList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : options.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
optionsList.add(name);
|
||||
if(null != value) optionsList.add(value);
|
||||
}
|
||||
return optionsList;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class YoutubeDLRequest {
|
||||
|
||||
private List<String> urls;
|
||||
private YoutubeDLOptions options = new YoutubeDLOptions();
|
||||
|
||||
public YoutubeDLRequest(String url) {
|
||||
this.urls = Arrays.asList(url);
|
||||
}
|
||||
|
||||
public YoutubeDLRequest(@NonNull List<String> urls) {
|
||||
this.urls = urls;
|
||||
}
|
||||
|
||||
public YoutubeDLRequest addOption(@NonNull String key, @NonNull String value){
|
||||
options.addOption(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public YoutubeDLRequest addOption(@NonNull String key, @NonNull Number value){
|
||||
options.addOption(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public YoutubeDLRequest addOption(String key){
|
||||
options.addOption(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getOption(String key){
|
||||
return options.getOption(key);
|
||||
}
|
||||
|
||||
public List<String> buildCommand(){
|
||||
List<String> command = new ArrayList<>();
|
||||
command.addAll(options.buildOptions());
|
||||
command.addAll(urls);
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class YoutubeDLResponse {
|
||||
|
||||
private List<String> command;
|
||||
private int exitCode;
|
||||
private String out;
|
||||
private String err;
|
||||
private long elapsedTime;
|
||||
|
||||
public YoutubeDLResponse(List<String> command, int exitCode, long elapsedTime, String out, String err) {
|
||||
this.command = command;
|
||||
this.elapsedTime = elapsedTime;
|
||||
this.exitCode = exitCode;
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
}
|
||||
|
||||
public List<String> getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public String getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public String getErr() {
|
||||
return err;
|
||||
}
|
||||
|
||||
public long getElapsedTime() {
|
||||
return elapsedTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.yausername.youtubedl_android.YoutubeDL.UpdateStatus;
|
||||
import com.yausername.youtubedl_common.SharedPrefsHelper;
|
||||
import com.yausername.youtubedl_common.utils.ZipUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
class YoutubeDLUpdater {
|
||||
|
||||
private YoutubeDLUpdater() {
|
||||
}
|
||||
|
||||
private static final String releasesUrl = "https://api.github.com/repos/xibr/ytdlp-lazy/releases/latest";
|
||||
private static final String youtubeDLVersionKey = "youtubeDLVersion";
|
||||
|
||||
static UpdateStatus update(Context appContext) throws IOException, YoutubeDLException {
|
||||
JsonNode json = checkForUpdate(appContext);
|
||||
if(null == json) return UpdateStatus.ALREADY_UP_TO_DATE;
|
||||
|
||||
String downloadUrl = getDownloadUrl(json);
|
||||
File file = download(appContext, downloadUrl);
|
||||
|
||||
File youtubeDLDir = null;
|
||||
try {
|
||||
youtubeDLDir = getYoutubeDLDir(appContext);
|
||||
//purge older version
|
||||
FileUtils.deleteDirectory(youtubeDLDir);
|
||||
//install newer version
|
||||
youtubeDLDir.mkdirs();
|
||||
ZipUtils.unzip(file, youtubeDLDir);
|
||||
} catch (Exception e) {
|
||||
//if something went wrong restore default version
|
||||
FileUtils.deleteQuietly(youtubeDLDir);
|
||||
YoutubeDL.getInstance().initYoutubeDL(appContext, youtubeDLDir);
|
||||
throw new YoutubeDLException(e);
|
||||
} finally {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
updateSharedPrefs(appContext, getTag(json));
|
||||
return UpdateStatus.DONE;
|
||||
}
|
||||
|
||||
private static void updateSharedPrefs(Context appContext, String tag) {
|
||||
SharedPrefsHelper.update(appContext, youtubeDLVersionKey, tag);
|
||||
}
|
||||
|
||||
private static JsonNode checkForUpdate(Context appContext) throws IOException {
|
||||
URL url = new URL(releasesUrl);
|
||||
JsonNode json = YoutubeDL.objectMapper.readTree(url);
|
||||
String newVersion = getTag(json);
|
||||
String oldVersion = SharedPrefsHelper.get(appContext, youtubeDLVersionKey);
|
||||
if(newVersion.equals(oldVersion)){
|
||||
return null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
private static String getTag(JsonNode json){
|
||||
return json.get("tag_name").asText();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getDownloadUrl(@NonNull JsonNode json) throws YoutubeDLException {
|
||||
ArrayNode assets = (ArrayNode) json.get("assets");
|
||||
String downloadUrl = "";
|
||||
for (JsonNode asset : assets) {
|
||||
if (YoutubeDL.youtubeDLFile.equals(asset.get("name").asText())) {
|
||||
downloadUrl = asset.get("browser_download_url").asText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (downloadUrl.isEmpty()) throw new YoutubeDLException("unable to get download url");
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static File download(Context appContext, String url) throws IOException {
|
||||
URL downloadUrl = new URL(url);
|
||||
File file = File.createTempFile("yt_dlp", "zip", appContext.getCacheDir());
|
||||
FileUtils.copyURLToFile(downloadUrl, file, 5000, 10000);
|
||||
return file;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static File getYoutubeDLDir(Context appContext) {
|
||||
File baseDir = new File(appContext.getNoBackupFilesDir(), YoutubeDL.baseName);
|
||||
return new File(baseDir, YoutubeDL.youtubeDLDirName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String version(Context appContext) {
|
||||
return SharedPrefsHelper.get(appContext, youtubeDLVersionKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
package com.yausername.youtubedl_android.mapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class VideoFormat {
|
||||
|
||||
private int asr;
|
||||
private int tbr;
|
||||
private int abr;
|
||||
private String format;
|
||||
@JsonProperty("format_id")
|
||||
private String formatId;
|
||||
@JsonProperty("format_note")
|
||||
private String formatNote;
|
||||
private String ext;
|
||||
private int preference;
|
||||
private String vcodec;
|
||||
private String acodec;
|
||||
private int width;
|
||||
private int height;
|
||||
private long filesize;
|
||||
private int fps;
|
||||
private String url;
|
||||
@JsonProperty("manifest_url")
|
||||
private String manifestUrl;
|
||||
@JsonProperty("http_headers")
|
||||
private Map<String, String> httpHeaders;
|
||||
|
||||
public int getAsr() {
|
||||
return asr;
|
||||
}
|
||||
|
||||
public int getTbr() {
|
||||
return tbr;
|
||||
}
|
||||
|
||||
public int getAbr() {
|
||||
return abr;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getFormatId() {
|
||||
return formatId;
|
||||
}
|
||||
|
||||
public String getFormatNote() {
|
||||
return formatNote;
|
||||
}
|
||||
|
||||
public String getExt() {
|
||||
return ext;
|
||||
}
|
||||
|
||||
public int getPreference() {
|
||||
return preference;
|
||||
}
|
||||
|
||||
public String getVcodec() {
|
||||
return vcodec;
|
||||
}
|
||||
|
||||
public String getAcodec() {
|
||||
return acodec;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public long getFilesize() {
|
||||
return filesize;
|
||||
}
|
||||
|
||||
public int getFps() {
|
||||
return fps;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getManifestUrl() {
|
||||
return manifestUrl;
|
||||
}
|
||||
|
||||
public Map<String, String> getHttpHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
package com.yausername.youtubedl_android.mapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class VideoInfo {
|
||||
|
||||
private String id;
|
||||
private String fulltitle;
|
||||
private String title;
|
||||
@JsonProperty("upload_date")
|
||||
private String uploadDate;
|
||||
@JsonProperty("display_id")
|
||||
private String displayId;
|
||||
private int duration;
|
||||
private String description;
|
||||
private String thumbnail;
|
||||
private String license;
|
||||
|
||||
@JsonProperty("view_count")
|
||||
private String viewCount;
|
||||
@JsonProperty("like_count")
|
||||
private String likeCount;
|
||||
@JsonProperty("dislike_count")
|
||||
private String dislikeCount;
|
||||
@JsonProperty("repost_count")
|
||||
private String repostCount;
|
||||
@JsonProperty("average_rating")
|
||||
private String averageRating;
|
||||
|
||||
|
||||
@JsonProperty("uploader_id")
|
||||
private String uploaderId;
|
||||
private String uploader;
|
||||
|
||||
@JsonProperty("player_url")
|
||||
private String playerUrl;
|
||||
@JsonProperty("webpage_url")
|
||||
private String webpageUrl;
|
||||
@JsonProperty("webpage_url_basename")
|
||||
private String webpageUrlBasename;
|
||||
|
||||
private String resolution;
|
||||
private int width;
|
||||
private int height;
|
||||
private String format;
|
||||
@JsonProperty("format_id")
|
||||
private String formatId;
|
||||
private String ext;
|
||||
|
||||
@JsonProperty("http_headers")
|
||||
private Map<String, String> httpHeaders;
|
||||
private ArrayList<String> categories;
|
||||
private ArrayList<String> tags;
|
||||
@JsonProperty("requested_formats")
|
||||
private ArrayList<VideoFormat> requestedFormats;
|
||||
private ArrayList<VideoFormat> formats;
|
||||
private ArrayList<VideoThumbnail> thumbnails;
|
||||
//private ArrayList<VideoSubtitle> subtitles;
|
||||
@JsonProperty("manifest_url")
|
||||
private String manifestUrl;
|
||||
private String url;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getFulltitle() {
|
||||
return fulltitle;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getUploadDate() {
|
||||
return uploadDate;
|
||||
}
|
||||
|
||||
public String getDisplayId() {
|
||||
return displayId;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getThumbnail() {
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
public String getViewCount() {
|
||||
return viewCount;
|
||||
}
|
||||
|
||||
public String getLikeCount() {
|
||||
return likeCount;
|
||||
}
|
||||
|
||||
public String getDislikeCount() {
|
||||
return dislikeCount;
|
||||
}
|
||||
|
||||
public String getRepostCount() {
|
||||
return repostCount;
|
||||
}
|
||||
|
||||
public String getAverageRating() {
|
||||
return averageRating;
|
||||
}
|
||||
|
||||
public String getUploaderId() {
|
||||
return uploaderId;
|
||||
}
|
||||
|
||||
public String getUploader() {
|
||||
return uploader;
|
||||
}
|
||||
|
||||
public String getPlayerUrl() {
|
||||
return playerUrl;
|
||||
}
|
||||
|
||||
public String getWebpageUrl() {
|
||||
return webpageUrl;
|
||||
}
|
||||
|
||||
public String getWebpageUrlBasename() {
|
||||
return webpageUrlBasename;
|
||||
}
|
||||
|
||||
public String getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public String getFormatId() {
|
||||
return formatId;
|
||||
}
|
||||
|
||||
public String getExt() {
|
||||
return ext;
|
||||
}
|
||||
|
||||
public Map<String, String> getHttpHeaders() {
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
public ArrayList<String> getCategories() {
|
||||
return categories;
|
||||
}
|
||||
|
||||
public ArrayList<String> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public ArrayList<VideoFormat> getRequestedFormats() {
|
||||
return requestedFormats;
|
||||
}
|
||||
|
||||
public ArrayList<VideoFormat> getFormats() {
|
||||
return formats;
|
||||
}
|
||||
|
||||
public ArrayList<VideoThumbnail> getThumbnails() {
|
||||
return thumbnails;
|
||||
}
|
||||
|
||||
public String getManifestUrl() {
|
||||
return manifestUrl;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.yausername.youtubedl_android.mapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class VideoSubtitle {
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.yausername.youtubedl_android.mapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class VideoThumbnail {
|
||||
private String url;
|
||||
private String id;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
<resources>
|
||||
<string name="app_name">youtubedl-android</string>
|
||||
</resources>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.yausername.youtubedl_android;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue