system overhaul / fixes / new features
- fixed titles in folders and gradle builds - converted downloading to a background service so that it continues even if the app is killed - converted listviews on home and history fragments with recyclerviews to increase speed - removed pull to refresh and floating action buttons for proper top app bar icons - added search functionality to history - added dialog if pressed delete all in history - made new downloads add to queue if a download is on progress - fixed leftover hardcoded strings - added sponsorblock functionality. Removed non music parts for audio files and added chapters for videos - fixed shimmer not removing when opening a video from share intent - showed no results image when history is empty or there are no results - made history cards clickable. You can delete them or open the link in youtube or copy the link to the clipboard
This commit is contained in:
parent
28a72866ca
commit
8c3525c1d5
34 changed files with 1211 additions and 688 deletions
|
|
@ -23,7 +23,7 @@
|
|||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/code/baseline_code_24.xml" />
|
||||
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/delete_sweep/baseline_delete_sweep_24.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
|
|
@ -33,9 +33,10 @@
|
|||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="assetSourceType" value="FILE" />
|
||||
<entry key="color" value="00a6ff" />
|
||||
<entry key="outputName" value="ic_code" />
|
||||
<entry key="sourceFile" value="C:\Users\denis\Desktop\youtubedl-android-0.13.1\youtubedl-android-0.13.1" />
|
||||
<entry key="outputName" value="ic_no_results" />
|
||||
<entry key="sourceFile" value="C:\Users\denis\Desktop\eaKdW401.svg" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
|
|
|
|||
|
|
@ -26,5 +26,10 @@
|
|||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -38,11 +38,14 @@
|
|||
<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_code.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_delete_all.xml" value="0.109" />
|
||||
<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_no_results.xml" value="0.109" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_refresh.xml" value="0.109" />
|
||||
<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_update.xml" value="0.1275" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_video.xml" value="0.1275" />
|
||||
|
|
@ -50,17 +53,19 @@
|
|||
<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.18" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_history.xml" value="0.2" />
|
||||
<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_bottom_sheet.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/history_no_results.xml" value="0.21" />
|
||||
<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/history_menu.xml" value="0.17" />
|
||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/menu/main_menu.xml" value="0.33" />
|
||||
<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.18" />
|
||||
</map>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.yausername.ytdl;
|
||||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
|
|
@ -18,7 +19,6 @@
|
|||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
|
|
@ -28,7 +28,10 @@
|
|||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".DownloaderService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<meta-data
|
||||
android:name = "ytAPIkey"
|
||||
android:value = "${ytAPI}"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
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;
|
||||
|
||||
|
|
@ -13,7 +9,6 @@ import com.google.android.material.color.DynamicColors;
|
|||
import com.yausername.ffmpeg.FFmpeg;
|
||||
import com.yausername.youtubedl_android.YoutubeDL;
|
||||
import com.yausername.youtubedl_android.YoutubeDLException;
|
||||
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
|
@ -25,15 +20,16 @@ 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;
|
||||
|
||||
public static NotificationUtil notificationUtil;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||
createNotificationChannel();
|
||||
|
||||
notificationUtil = new NotificationUtil(this);
|
||||
createNotificationChannels();
|
||||
|
||||
configureRxJavaErrorHandler();
|
||||
Completable.fromAction(this::initLibraries).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
|
||||
@Override
|
||||
|
|
@ -71,15 +67,7 @@ public class App extends Application {
|
|||
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);
|
||||
}
|
||||
private void createNotificationChannels() {
|
||||
notificationUtil.createNotificationChannel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
app/src/main/java/com/deniscerri/ytdl/DownloaderService.java
Normal file
44
app/src/main/java/com/deniscerri/ytdl/DownloaderService.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
public class DownloaderService extends Service {
|
||||
|
||||
private LocalBinder binder = new LocalBinder();
|
||||
private NotificationCompat.Builder builder;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
Intent theIntent = new Intent(this, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
String title = intent.getStringExtra("title");
|
||||
|
||||
Notification notification = App.notificationUtil.createDownloadServiceNotification(pendingIntent,title);
|
||||
startForeground(NotificationUtil.DOWNLOAD_NOTIFICATION_ID, notification);
|
||||
return binder;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
|
||||
DownloaderService getService() {
|
||||
return DownloaderService.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +1,64 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
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.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.deniscerri.ytdl.adapter.HistoryRecyclerViewAdapter;
|
||||
import com.deniscerri.ytdl.database.DBManager;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
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 com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
*/
|
||||
public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||
public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdapter.OnItemClickListener, View.OnClickListener{
|
||||
|
||||
private LinearLayout linearLayout;
|
||||
private ScrollView scrollView;
|
||||
private View fragmentView;
|
||||
private DBManager dbManager;
|
||||
Context context;
|
||||
private LayoutInflater layoutinflater;
|
||||
private ShimmerFrameLayout shimmerCards;
|
||||
private MaterialToolbar topAppBar;
|
||||
private RecyclerView recyclerView;
|
||||
private HistoryRecyclerViewAdapter historyRecyclerViewAdapter;
|
||||
private BottomSheetDialog bottomSheet;
|
||||
|
||||
private ArrayList<Video> historyObjects;
|
||||
private static final String TAG = "HistoryFragment";
|
||||
|
||||
private static final String youtubeVideoURL = "https://www.youtube.com/watch?v=";
|
||||
|
||||
public HistoryFragment() {
|
||||
}
|
||||
|
|
@ -75,31 +80,23 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
fragmentView = inflater.inflate(R.layout.fragment_history, container, false);
|
||||
context = fragmentView.getContext();
|
||||
layoutinflater = LayoutInflater.from(context);
|
||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
||||
linearLayout = fragmentView.findViewById(R.id.historylinearLayout1);
|
||||
shimmerCards = fragmentView.findViewById(R.id.shimmer_history_framelayout);
|
||||
topAppBar = fragmentView.findViewById(R.id.history_toolbar);
|
||||
|
||||
dbManager = new DBManager(context);
|
||||
historyObjects = new ArrayList<>();
|
||||
|
||||
SwipeRefreshLayout swipeRefreshLayout = fragmentView.findViewById(R.id.swiperefresh);
|
||||
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
initCards();
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
});
|
||||
recyclerView = fragmentView.findViewById(R.id.recycler_view_history);
|
||||
|
||||
historyRecyclerViewAdapter = new HistoryRecyclerViewAdapter(historyObjects, this, context);
|
||||
recyclerView.setAdapter(historyRecyclerViewAdapter);
|
||||
recyclerView.setNestedScrollingEnabled(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;
|
||||
|
|
@ -109,24 +106,26 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
|||
private void initCards(){
|
||||
shimmerCards.startShimmer();
|
||||
shimmerCards.setVisibility(View.VISIBLE);
|
||||
historyRecyclerViewAdapter.clear();
|
||||
linearLayout.removeAllViews();
|
||||
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);
|
||||
historyObjects = dbManager.getHistory("");
|
||||
|
||||
uiHandler.post(() -> {
|
||||
linearLayout.addView(padding);
|
||||
historyRecyclerViewAdapter.setVideoList(historyObjects);
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
});
|
||||
|
||||
if(historyObjects.size() == 0){
|
||||
uiHandler.post(() -> {
|
||||
RelativeLayout no_results = new RelativeLayout(context);
|
||||
layoutinflater.inflate(R.layout.history_no_results, no_results);
|
||||
linearLayout.addView(no_results);
|
||||
});
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}catch(Exception e){
|
||||
|
|
@ -136,63 +135,87 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
|||
}
|
||||
|
||||
public void scrollToTop(){
|
||||
scrollView.smoothScrollTo(0,0);
|
||||
recyclerView.smoothScrollToPosition(0);
|
||||
}
|
||||
|
||||
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 = card.findViewById(R.id.history_image_view);
|
||||
String imageURL= video.getThumb();
|
||||
|
||||
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 = card.findViewById(R.id.history_title);
|
||||
String title = video.getTitle();
|
||||
|
||||
if(title.length() > 100){
|
||||
title = title.substring(0, 40) + "...";
|
||||
}
|
||||
videoTitle.setText(title);
|
||||
|
||||
// Bottom Info ----------------------------------
|
||||
TextView bottomInfo = card.findViewById(R.id.history_info_bottom);
|
||||
String info = video.getAuthor() + " • " + video.getDuration();
|
||||
bottomInfo.setText(info);
|
||||
|
||||
// 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")){
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_music));
|
||||
}else{
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_video));
|
||||
}
|
||||
|
||||
uiHandler.post(() -> linearLayout.addView(r));
|
||||
private void addNoResultsView(){
|
||||
RelativeLayout no_results = new RelativeLayout(context);
|
||||
layoutinflater.inflate(R.layout.history_no_results, no_results);
|
||||
linearLayout.addView(no_results);
|
||||
}
|
||||
|
||||
private void initMenu(){
|
||||
topAppBar.setOnClickListener(view -> {
|
||||
scrollToTop();
|
||||
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
topAppBar.getMenu().findItem(R.id.search_history).setOnActionExpandListener(onActionExpandListener);
|
||||
SearchView searchView = (SearchView) topAppBar.getMenu().findItem(R.id.search_history).getActionView();
|
||||
searchView.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
searchView.setQueryHint(getString(R.string.search_history_hint));
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
topAppBar.getMenu().findItem(R.id.search_history).collapseActionView();
|
||||
historyObjects = dbManager.getHistory(query);
|
||||
historyRecyclerViewAdapter.clear();
|
||||
historyRecyclerViewAdapter.setVideoList(historyObjects);
|
||||
|
||||
if(historyObjects.size() == 0) addNoResultsView();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
|
||||
historyObjects = dbManager.getHistory(newText);
|
||||
historyRecyclerViewAdapter.clear();
|
||||
historyRecyclerViewAdapter.setVideoList(historyObjects);
|
||||
|
||||
if (historyObjects.size() == 0) {
|
||||
linearLayout.removeAllViews();
|
||||
addNoResultsView();
|
||||
}else linearLayout.removeAllViews();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
topAppBar.setOnClickListener(view -> scrollToTop());
|
||||
|
||||
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
||||
switch (m.getItemId()){
|
||||
case R.id.delete_history:
|
||||
dbManager.clearHistory();
|
||||
linearLayout.removeAllViews();
|
||||
if(historyObjects.size() == 0){
|
||||
Toast.makeText(context, "History is Empty!", Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder delete_dialog = new MaterialAlertDialogBuilder(context);
|
||||
delete_dialog.setTitle(getString(R.string.confirm_delete_history));
|
||||
delete_dialog.setMessage(getString(R.string.confirm_delete_history_desc));
|
||||
delete_dialog.setNegativeButton(getString(R.string.cancel), (dialogInterface, i) -> {
|
||||
dialogInterface.cancel();
|
||||
});
|
||||
delete_dialog.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> {
|
||||
dbManager.clearHistory();
|
||||
historyRecyclerViewAdapter.clear();
|
||||
addNoResultsView();
|
||||
});
|
||||
delete_dialog.show();
|
||||
return true;
|
||||
case R.id.refresh_history:
|
||||
historyRecyclerViewAdapter.clear();
|
||||
initCards();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -202,9 +225,86 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.fab_history) {
|
||||
scrollView.smoothScrollBy(0, 0);
|
||||
scrollView.smoothScrollTo(0, 0);
|
||||
int id = v.getId();
|
||||
switch (id){
|
||||
case R.id.bottomsheet_remove_button:
|
||||
removeHistoryItem((Integer) v.getTag());
|
||||
break;
|
||||
case R.id.bottom_sheet_link:
|
||||
copyLinkToClipBoard((Integer) v.getTag());
|
||||
break;
|
||||
case R.id.bottomsheet_open_link_button:
|
||||
openLinkIntent((Integer) v.getTag());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeHistoryItem(int position){
|
||||
if(bottomSheet != null) bottomSheet.hide();
|
||||
|
||||
Video v = historyObjects.get(position);
|
||||
MaterialAlertDialogBuilder delete_dialog = new MaterialAlertDialogBuilder(context);
|
||||
delete_dialog.setTitle(getString(R.string.confirm_delete_history));
|
||||
delete_dialog.setMessage("You are going to delete \""+v.getTitle()+"\"!");
|
||||
delete_dialog.setNegativeButton(getString(R.string.cancel), (dialogInterface, i) -> {
|
||||
dialogInterface.cancel();
|
||||
});
|
||||
delete_dialog.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> {
|
||||
historyObjects.remove(position);
|
||||
historyRecyclerViewAdapter.notifyItemRemoved(position);
|
||||
historyRecyclerViewAdapter.notifyItemRangeChanged(position, historyObjects.size());
|
||||
dbManager.clearHistoryItem(v);
|
||||
});
|
||||
delete_dialog.show();
|
||||
}
|
||||
|
||||
private void copyLinkToClipBoard(int position){
|
||||
String url = youtubeVideoURL + historyObjects.get(position).getVideoId();
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("Youtube URL", url);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
if(bottomSheet != null) bottomSheet.hide();
|
||||
Toast.makeText(context, "Link copied to clipboard", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
private void openLinkIntent(int position){
|
||||
String url = youtubeVideoURL + historyObjects.get(position).getVideoId();
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
if(bottomSheet != null) bottomSheet.hide();
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCardClick(int position) {
|
||||
bottomSheet = new BottomSheetDialog(context);
|
||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
bottomSheet.setContentView(R.layout.history_bottom_sheet);
|
||||
Video video = historyObjects.get(position);
|
||||
|
||||
TextView title = bottomSheet.findViewById(R.id.bottom_sheet_title);
|
||||
title.setText(video.getTitle());
|
||||
|
||||
TextView author = bottomSheet.findViewById(R.id.bottom_sheet_author);
|
||||
author.setText(video.getAuthor());
|
||||
|
||||
Button link = bottomSheet.findViewById(R.id.bottom_sheet_link);
|
||||
String url = youtubeVideoURL+video.getVideoId();
|
||||
link.setText(url);
|
||||
link.setTag(position);
|
||||
link.setOnClickListener(this);
|
||||
|
||||
Button remove = bottomSheet.findViewById(R.id.bottomsheet_remove_button);
|
||||
remove.setTag(position);
|
||||
remove.setOnClickListener(this);
|
||||
|
||||
Button openLink = bottomSheet.findViewById(R.id.bottomsheet_open_link_button);
|
||||
openLink.setTag(position);
|
||||
openLink.setOnClickListener(this);
|
||||
|
||||
bottomSheet.show();
|
||||
bottomSheet.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -9,25 +9,20 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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 android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -37,17 +32,17 @@ import androidx.core.app.ActivityCompat;
|
|||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import com.deniscerri.ytdl.adapter.HomeRecyclerViewAdapter;
|
||||
import com.deniscerri.ytdl.api.YoutubeAPIManager;
|
||||
import com.deniscerri.ytdl.database.DBManager;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
import com.facebook.shimmer.ShimmerFrameLayout;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.yausername.youtubedl_android.DownloadProgressCallback;
|
||||
import com.yausername.youtubedl_android.YoutubeDL;
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest;
|
||||
|
|
@ -70,15 +65,18 @@ import io.reactivex.disposables.Disposable;
|
|||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
|
||||
public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||
public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.OnItemClickListener, View.OnClickListener {
|
||||
private boolean downloading = false;
|
||||
private View fragmentView;
|
||||
private ProgressBar progressBar;
|
||||
private String inputQuery;
|
||||
private LinearLayout linearLayout;
|
||||
private ScrollView scrollView;
|
||||
private LinearLayout homeLinearLayout;
|
||||
private RecyclerView recyclerView;
|
||||
private HomeRecyclerViewAdapter homeRecyclerViewAdapter;
|
||||
private NestedScrollView scrollView;
|
||||
private ShimmerFrameLayout shimmerCards;
|
||||
private LayoutInflater layoutinflater;
|
||||
|
||||
Context context;
|
||||
Activity activity;
|
||||
private MaterialToolbar topAppBar;
|
||||
|
|
@ -86,33 +84,22 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
private static final String TAG = "HomeFragment";
|
||||
|
||||
private ArrayList<Video> resultObjects;
|
||||
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private DBManager dbManager;
|
||||
private YoutubeAPIManager youtubeAPIManager;
|
||||
private NotificationCompat.Builder download_notification;
|
||||
private NotificationManagerCompat notificationManager;
|
||||
private Queue<Video> downloadQueue;
|
||||
|
||||
MainActivity mainActivity;
|
||||
private static final NotificationUtil notificationUtil = App.notificationUtil;
|
||||
|
||||
private final DownloadProgressCallback callback = new DownloadProgressCallback() {
|
||||
@Override
|
||||
public void onProgressUpdate(float progress, long etaInSeconds, String line) {
|
||||
activity.runOnUiThread(() -> {
|
||||
progressBar.setProgress((int) progress);
|
||||
String contentText = "";
|
||||
|
||||
if(downloadQueue.size() > 0){
|
||||
contentText += " " + downloadQueue.size() + " items left\n";
|
||||
}
|
||||
|
||||
contentText += line.replaceAll("\\[.*?\\]", "");
|
||||
|
||||
|
||||
download_notification.setProgress(100,(int) progress,false)
|
||||
.setContentText(contentText);
|
||||
notificationManager.notify(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID), download_notification.build());
|
||||
|
||||
notificationUtil.updateDownloadNotification(NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
|
||||
line, (int) progress, downloadQueue.size(), downloadQueue.peek().getTitle());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -129,81 +116,51 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
public String convertETASecondsToTime(long eta){
|
||||
String time = "";
|
||||
int s = (int) eta;
|
||||
int h = s / 3600;
|
||||
int m = (s % 3600) / 60;
|
||||
s %= 60;
|
||||
if(h > 0){
|
||||
time+=h+"hr ";
|
||||
}else if(m > 0){
|
||||
if(m < 10) time +="0";
|
||||
time+=m+"min";
|
||||
}
|
||||
if(s < 10 && s > 0 && m > 0) time +="0";
|
||||
if(s < 0) s = 0;
|
||||
time+=s+"sec";
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
downloadQueue = new LinkedList<>();
|
||||
resultObjects = new ArrayList<>();
|
||||
|
||||
// Inflate the layout for this fragment
|
||||
fragmentView = inflater.inflate(R.layout.fragment_home, container, false);
|
||||
context = fragmentView.getContext();
|
||||
activity = getActivity();
|
||||
mainActivity = (MainActivity) activity;
|
||||
layoutinflater = LayoutInflater.from(context);
|
||||
|
||||
|
||||
//initViews
|
||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
||||
homeLinearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||
shimmerCards = fragmentView.findViewById(R.id.shimmer_results_framelayout);
|
||||
topAppBar = fragmentView.findViewById(R.id.home_toolbar);
|
||||
scrollView = fragmentView.findViewById(R.id.home_scrollview);
|
||||
recyclerView = fragmentView.findViewById(R.id.recycler_view_home);
|
||||
|
||||
downloadQueue = new LinkedList<>();
|
||||
|
||||
SwipeRefreshLayout swipeRefreshLayout = fragmentView.findViewById(R.id.swiperefreshhome);
|
||||
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
initCards();
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
});
|
||||
|
||||
FloatingActionButton fab = fragmentView.findViewById(R.id.fab_home);
|
||||
fab.setOnClickListener(this);
|
||||
|
||||
scrollView.setOnScrollChangeListener((view, x, y, oldX, oldY) -> {
|
||||
if(y > 500){
|
||||
fab.show();
|
||||
}else{
|
||||
fab.hide();
|
||||
}
|
||||
});
|
||||
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(resultObjects, this, activity);
|
||||
recyclerView.setAdapter(homeRecyclerViewAdapter);
|
||||
recyclerView.setNestedScrollingEnabled(false);
|
||||
|
||||
initMenu();
|
||||
|
||||
if(inputQuery != null){
|
||||
if (inputQuery != null) {
|
||||
parseQuery();
|
||||
inputQuery = null;
|
||||
}else{
|
||||
} else {
|
||||
initCards();
|
||||
}
|
||||
|
||||
return fragmentView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void initCards(){
|
||||
private void initCards() {
|
||||
shimmerCards.startShimmer();
|
||||
shimmerCards.setVisibility(View.VISIBLE);
|
||||
linearLayout.removeAllViews();
|
||||
try{
|
||||
homeRecyclerViewAdapter.clear();
|
||||
homeLinearLayout.removeAllViews();
|
||||
try {
|
||||
Thread thread = new Thread(() -> {
|
||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
dbManager = new DBManager(context);
|
||||
|
|
@ -211,51 +168,40 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
resultObjects = dbManager.getResults();
|
||||
boolean trending = false;
|
||||
|
||||
if(resultObjects.size() == 0){
|
||||
if (resultObjects.size() == 0) {
|
||||
trending = true;
|
||||
try {
|
||||
resultObjects = youtubeAPIManager.getTrending(context);
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
TextView trendingText = new TextView(context);
|
||||
|
||||
if(resultObjects != null){
|
||||
if (resultObjects != null) {
|
||||
scrollToTop();
|
||||
|
||||
if(trending){
|
||||
trendingText.setText(R.string.Trending);
|
||||
trendingText.setPadding(30, 0 ,0 ,0);
|
||||
uiHandler.post(() -> linearLayout.addView(trendingText));
|
||||
if (trending) {
|
||||
uiHandler.post(this::addTrendingText);
|
||||
|
||||
}else{
|
||||
if(resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1){
|
||||
} else {
|
||||
if (resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1) {
|
||||
createDownloadAllCard();
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < resultObjects.size(); i++){
|
||||
createCard(resultObjects.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
uiHandler.post(() -> {
|
||||
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
if(resultObjects.size() > 1){
|
||||
addEndofResultsText();
|
||||
}
|
||||
});
|
||||
});
|
||||
thread.start();
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void initMenu(){
|
||||
private void initMenu() {
|
||||
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
||||
|
|
@ -290,31 +236,36 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
topAppBar.setOnClickListener(view -> scrollToTop());
|
||||
|
||||
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
||||
if (m.getItemId() == R.id.delete_results) {
|
||||
dbManager.clearResults();
|
||||
linearLayout.removeAllViews();
|
||||
initCards();
|
||||
return true;
|
||||
switch (m.getItemId()){
|
||||
case R.id.delete_results:
|
||||
dbManager.clearResults();
|
||||
recyclerView.removeAllViews();
|
||||
initCards();
|
||||
return true;
|
||||
case R.id.refresh_results:
|
||||
recyclerView.removeAllViews();
|
||||
initCards();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void handleIntent(Intent intent){
|
||||
public void handleIntent(Intent intent) {
|
||||
inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
}
|
||||
|
||||
public void scrollToTop(){
|
||||
scrollView.smoothScrollTo(0,0);
|
||||
public void scrollToTop() {
|
||||
scrollView.smoothScrollTo(-100,-100);
|
||||
new Handler(Looper.getMainLooper()).post(() -> ((AppBarLayout) topAppBar.getParent()).setExpanded(true, true));
|
||||
}
|
||||
|
||||
private void parseQuery() {
|
||||
shimmerCards.startShimmer();
|
||||
shimmerCards.setVisibility(View.VISIBLE);
|
||||
linearLayout.removeAllViews();
|
||||
homeRecyclerViewAdapter.clear();
|
||||
homeLinearLayout.removeAllViews();
|
||||
|
||||
resultObjects = new ArrayList<>();
|
||||
dbManager = new DBManager(context);
|
||||
youtubeAPIManager = new YoutubeAPIManager(context);
|
||||
scrollToTop();
|
||||
|
|
@ -323,38 +274,37 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
Pattern p = Pattern.compile("^(https?)://(www.)?youtu(.be)?");
|
||||
Matcher m = p.matcher(inputQuery);
|
||||
|
||||
if(m.find()){
|
||||
if (m.find()) {
|
||||
type = "Video";
|
||||
if (inputQuery.contains("playlist?list=")) {
|
||||
type = "Playlist";
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, inputQuery + " "+ type);
|
||||
Log.e(TAG, inputQuery + " " + type);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case "Search": {
|
||||
Thread thread = new Thread(new Runnable(){
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
private final String query;
|
||||
|
||||
{
|
||||
this.query = inputQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
public void run() {
|
||||
try {
|
||||
resultObjects = youtubeAPIManager.search(query);
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
Log.e(TAG, resultObjects.toString());
|
||||
|
||||
for(int i = 0; i < resultObjects.size(); i++){
|
||||
createCard(resultObjects.get(i));
|
||||
}
|
||||
dbManager.addToResults(resultObjects);
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
addEndofResultsText();
|
||||
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||
//addEndofResultsText();
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
});
|
||||
|
|
@ -362,36 +312,40 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
});
|
||||
thread.start();
|
||||
break;
|
||||
}case "Video": {
|
||||
}
|
||||
case "Video": {
|
||||
String[] el = inputQuery.split("/");
|
||||
inputQuery = el[el.length -1];
|
||||
inputQuery = el[el.length - 1];
|
||||
|
||||
if(inputQuery.contains("watch?v=")){
|
||||
if (inputQuery.contains("watch?v=")) {
|
||||
inputQuery = inputQuery.substring(8);
|
||||
}
|
||||
|
||||
if(inputQuery.contains("&list=")){
|
||||
if (inputQuery.contains("&list=")) {
|
||||
el = inputQuery.split("&list=");
|
||||
inputQuery = el[0];
|
||||
}
|
||||
|
||||
Thread thread = new Thread(new Runnable(){
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
private final String query;
|
||||
|
||||
{
|
||||
this.query = inputQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
public void run() {
|
||||
try {
|
||||
resultObjects.add(youtubeAPIManager.getVideo(query));
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
|
||||
createCard(resultObjects.get(0));
|
||||
|
||||
dbManager.addToResults(resultObjects);
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
});
|
||||
|
|
@ -399,31 +353,32 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
});
|
||||
thread.start();
|
||||
break;
|
||||
}case "Playlist": {
|
||||
}
|
||||
case "Playlist": {
|
||||
inputQuery = inputQuery.split("list=")[1];
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
private final String query;
|
||||
|
||||
{
|
||||
this.query = inputQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
public void run() {
|
||||
try {
|
||||
resultObjects = youtubeAPIManager.getPlaylist(query, "");
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
dbManager.addToResults(resultObjects);
|
||||
// DOWNLOAD ALL BUTTON
|
||||
if(resultObjects.size() > 1){
|
||||
if (resultObjects.size() > 1) {
|
||||
createDownloadAllCard();
|
||||
}
|
||||
for(int i = 0 ; i < resultObjects.size(); i++){
|
||||
createCard(resultObjects.get(i));
|
||||
}
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
addEndofResultsText();
|
||||
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||
//addEndofResultsText();
|
||||
shimmerCards.stopShimmer();
|
||||
shimmerCards.setVisibility(View.GONE);
|
||||
});
|
||||
|
|
@ -433,23 +388,20 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
break;
|
||||
}
|
||||
}
|
||||
}catch(Exception e){
|
||||
Log.e(TAG, e.toString());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void addEndofResultsText(){
|
||||
TextView padding = new TextView(context);
|
||||
int dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, fragmentView.getResources().getDisplayMetrics());
|
||||
padding.setHeight(dp);
|
||||
padding.setText(R.string.end_of_results);
|
||||
padding.setGravity(Gravity.CENTER);
|
||||
linearLayout.addView(padding);
|
||||
private void addTrendingText(){
|
||||
TextView trendingText = new TextView(context);
|
||||
trendingText.setText(R.string.Trending);
|
||||
trendingText.setPadding(70, 0 , 0, 0);
|
||||
homeLinearLayout.addView(trendingText);
|
||||
}
|
||||
|
||||
private void createDownloadAllCard(){
|
||||
private void createDownloadAllCard() {
|
||||
RelativeLayout r = new RelativeLayout(context);
|
||||
layoutinflater.inflate(R.layout.download_all_card, r);
|
||||
|
||||
|
|
@ -466,116 +418,13 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
videoBtn.setOnClickListener(this);
|
||||
|
||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
uiHandler.post(() -> linearLayout.addView(r));
|
||||
uiHandler.post(() -> homeLinearLayout.addView(r));
|
||||
}
|
||||
|
||||
private void createCard(Video video){
|
||||
RelativeLayout r = new RelativeLayout(context);
|
||||
layoutinflater.inflate(R.layout.result_card, r);
|
||||
|
||||
CardView card = r.findViewById(R.id.result_card_view);
|
||||
// THUMBNAIL ----------------------------------
|
||||
ImageView thumbnail = card.findViewById(R.id.result_image_view);
|
||||
String imageURL= video.getThumb();
|
||||
|
||||
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 = card.findViewById(R.id.result_title);
|
||||
String title = video.getTitle();
|
||||
|
||||
if(title.length() > 100){
|
||||
title = title.substring(0, 40) + "...";
|
||||
}
|
||||
videoTitle.setText(title);
|
||||
|
||||
// Bottom Info ----------------------------------
|
||||
|
||||
TextView bottomInfo = card.findViewById(R.id.result_info_bottom);
|
||||
String info = video.getAuthor() + " • " + video.getDuration();
|
||||
bottomInfo.setText(info);
|
||||
|
||||
// BUTTONS ----------------------------------
|
||||
String videoID = video.getVideoId();
|
||||
|
||||
LinearLayout buttonLayout = card.findViewById(R.id.download_button_layout);
|
||||
|
||||
MaterialButton musicBtn = buttonLayout.findViewById(R.id.download_music);
|
||||
musicBtn.setTag(videoID + "##mp3");
|
||||
|
||||
MaterialButton videoBtn = buttonLayout.findViewById(R.id.download_video);
|
||||
videoBtn.setTag(videoID + "##mp4");
|
||||
|
||||
musicBtn.setOnClickListener(this);
|
||||
videoBtn.setOnClickListener(this);
|
||||
|
||||
// PROGRESS BAR ----------------------------------------------------
|
||||
|
||||
ProgressBar progressBar = card.findViewById(R.id.download_progress);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
progressBar.setTag(videoID + "##progress");
|
||||
|
||||
if(video.isAudioDownloaded() == 1){
|
||||
musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
|
||||
}
|
||||
if(video.isVideoDownloaded() == 1){
|
||||
videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
|
||||
}
|
||||
|
||||
card.setTag(videoID + "##card");
|
||||
uiHandler.post(() -> linearLayout.addView(r));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//do what you want to do when button is clicked
|
||||
String viewIdName;
|
||||
try{
|
||||
viewIdName = v.getTag().toString();
|
||||
}catch(Exception e){
|
||||
viewIdName = "";
|
||||
}
|
||||
|
||||
if(!viewIdName.isEmpty()){
|
||||
if(viewIdName.contains("mp3") || viewIdName.contains("mp4")){
|
||||
Log.e(TAG, viewIdName);
|
||||
String[] buttonData = viewIdName.split("##");
|
||||
if(buttonData[0].equals("ALL")){
|
||||
for (int i = 0; i < resultObjects.size(); i++){
|
||||
Video vid = findVideo(resultObjects.get(i).getVideoId());
|
||||
vid.setDownloadedType(buttonData[1]);
|
||||
downloadQueue.add(vid);
|
||||
}
|
||||
|
||||
}else{
|
||||
Video vid = findVideo(buttonData[0]);
|
||||
vid.setDownloadedType(buttonData[1]);
|
||||
downloadQueue.add(vid);
|
||||
}
|
||||
|
||||
if (downloading) {
|
||||
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
startDownload(downloadQueue);
|
||||
|
||||
}
|
||||
}else{
|
||||
if (v.getId() == R.id.fab_home) {
|
||||
scrollView.smoothScrollBy(0, 0);
|
||||
scrollToTop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Video findVideo(String id){
|
||||
for(int i = 0; i < resultObjects.size(); i++){
|
||||
public Video findVideo(String id) {
|
||||
for (int i = 0; i < resultObjects.size(); i++) {
|
||||
Video v = resultObjects.get(i);
|
||||
if((v.getVideoId()).equals(id)){
|
||||
if ((v.getVideoId()).equals(id)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
|
@ -584,20 +433,25 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void startDownload(Queue<Video> videos) {
|
||||
Video video;
|
||||
try{
|
||||
if(videos.size() == 0){
|
||||
mainActivity.stopDownloadService();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
video = videos.remove();
|
||||
}catch(Exception e){
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isStoragePermissionGranted()) {
|
||||
Toast.makeText(context, R.string.try_again_after_permission, Toast.LENGTH_LONG).show();
|
||||
mainActivity.stopDownloadService();
|
||||
return;
|
||||
}
|
||||
|
||||
String id = video.getVideoId();
|
||||
String url = "https://www.youtube.com/watch?v=" + id;
|
||||
YoutubeDLRequest request = new YoutubeDLRequest(url);
|
||||
|
|
@ -608,7 +462,7 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
|
||||
MaterialButton clickedButton = null;
|
||||
|
||||
if(type.equals("mp3")){
|
||||
if (type.equals("mp3")) {
|
||||
request.addOption("--embed-thumbnail");
|
||||
request.addOption("--sponsorblock-remove", "all");
|
||||
request.addOption("--postprocessor-args", "-write_id3v1 1 -id3v2_version 3");
|
||||
|
|
@ -617,25 +471,24 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
request.addOption("-x");
|
||||
request.addOption("--audio-format", "mp3");
|
||||
|
||||
clickedButton = linearLayout.findViewWithTag(id+"##mp3");
|
||||
}else if(type.equals("mp4")){
|
||||
clickedButton = recyclerView.findViewWithTag(id + "##mp3");
|
||||
} else if (type.equals("mp4")) {
|
||||
request.addOption("--embed-thumbnail");
|
||||
request.addOption("--sponsorblock-mark", "all");
|
||||
request.addOption("--embed-subs", "");
|
||||
request.addOption("-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best");
|
||||
|
||||
clickedButton = linearLayout.findViewWithTag(id+"##mp4");
|
||||
clickedButton = recyclerView.findViewWithTag(id + "##mp4");
|
||||
}
|
||||
request.addOption("-o", youtubeDLDir.getAbsolutePath() + "/%(title)s.%(ext)s");
|
||||
|
||||
progressBar = fragmentView.findViewWithTag(id+"##progress");
|
||||
progressBar = fragmentView.findViewWithTag(id + "##progress");
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
//scroll to Card
|
||||
View view = fragmentView.findViewWithTag(id+"##progress");
|
||||
view.getParent().requestChildFocus(view,view);
|
||||
|
||||
showStart(video);
|
||||
View view = fragmentView.findViewWithTag(id + "##progress");
|
||||
scrollView.requestChildFocus(view, view);
|
||||
progressBar.setProgress(0);
|
||||
downloading = true;
|
||||
|
||||
Video theVideo = video;
|
||||
|
|
@ -647,41 +500,34 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
progressBar.setProgress(0);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
notificationManager.cancel(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID));
|
||||
download_notification.setContentText(getString(R.string.download_success))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), android.R.drawable.stat_sys_download_done))
|
||||
.setProgress(0,0,false);
|
||||
notificationManager.notify(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID), download_notification.build());
|
||||
//TODO show download finished
|
||||
|
||||
if(theClickedButton != null){
|
||||
if(type.equals("mp3")){
|
||||
if (theClickedButton != null) {
|
||||
if (type.equals("mp3")) {
|
||||
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
|
||||
}else{
|
||||
} else {
|
||||
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addToHistory(theVideo, new Date());
|
||||
updateDownloadStatusOnResult(theVideo, type);
|
||||
downloading = false;
|
||||
|
||||
|
||||
// MEDIA SCAN
|
||||
MediaScannerConnection.scanFile(context, new String[]{youtubeDLDir.getAbsolutePath()}, null, null);
|
||||
|
||||
// SCAN NEXT IN QUEUE
|
||||
startDownload(videos);
|
||||
}, e -> {
|
||||
if(BuildConfig.DEBUG) Log.e(TAG, getString(R.string.failed_download), e);
|
||||
if (BuildConfig.DEBUG) Log.e(TAG, getString(R.string.failed_download), e);
|
||||
Toast.makeText(context, R.string.failed_download, Toast.LENGTH_LONG).show();
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
download_notification.setContentText(getString(R.string.failed_download))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setProgress(0,0,false);
|
||||
notificationManager.notify(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID), download_notification.build());
|
||||
|
||||
//TODO notification failed
|
||||
downloading = false;
|
||||
|
||||
// SCAN NEXT IN QUEUE
|
||||
|
|
@ -691,7 +537,7 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
}
|
||||
|
||||
|
||||
public void addToHistory(Video video, Date date){
|
||||
public void addToHistory(Video video, Date date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
int day = cal.get(Calendar.DAY_OF_MONTH);
|
||||
|
|
@ -703,80 +549,46 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
String time = formatter.format(date);
|
||||
String downloadedTime = day + " " + month + " " + year + " " + time;
|
||||
|
||||
if(video != null){
|
||||
if (video != null) {
|
||||
dbManager = new DBManager(context);
|
||||
try{
|
||||
try {
|
||||
video.setDownloadedTime(downloadedTime);
|
||||
dbManager.addToHistory(video);
|
||||
}catch(Exception ignored){}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDownloadStatusOnResult(Video v, String type){
|
||||
if(v != null){
|
||||
public void updateDownloadStatusOnResult(Video v, String type) {
|
||||
if (v != null) {
|
||||
dbManager = new DBManager(context);
|
||||
try{
|
||||
try {
|
||||
dbManager.updateDownloadStatusOnResult(v.getVideoId(), type);
|
||||
}catch(Exception ignored){}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
compositeDisposable.dispose();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private File getDownloadLocation(String type) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
|
||||
String downloadsDir;
|
||||
if(type.equals("mp3")){
|
||||
if (type.equals("mp3")) {
|
||||
downloadsDir = sharedPreferences.getString("music_path", "");
|
||||
}else{
|
||||
} else {
|
||||
downloadsDir = sharedPreferences.getString("video_path", "");
|
||||
}
|
||||
File youtubeDLDir = new File(downloadsDir);
|
||||
if (!youtubeDLDir.exists()){
|
||||
if (!youtubeDLDir.exists()) {
|
||||
boolean isDirCreated = youtubeDLDir.mkdir();
|
||||
if(!isDirCreated){
|
||||
if (!isDirCreated) {
|
||||
Toast.makeText(context, R.string.failed_making_directory, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
return youtubeDLDir;
|
||||
}
|
||||
|
||||
private void showStart(Video video) {
|
||||
progressBar.setProgress(0);
|
||||
|
||||
//NOTIFICATION BUILDER
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent pendingIntent;
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
int PROGRESS_MAX = 100;
|
||||
int PROGRESS_CURR = progressBar.getProgress();
|
||||
|
||||
download_notification = new NotificationCompat.Builder(context, App.DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), android.R.drawable.stat_sys_download))
|
||||
.setContentTitle(video.getTitle())
|
||||
.setContentText("")
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false);
|
||||
|
||||
notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationManager.notify(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID), download_notification.build());
|
||||
}
|
||||
|
||||
public boolean isStoragePermissionGranted() {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
|
|
@ -787,4 +599,56 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onButtonClick(int position, String type) {
|
||||
Log.e(TAG, type);
|
||||
Video vid = resultObjects.get(position);
|
||||
vid.setDownloadedType(type);
|
||||
|
||||
downloadQueue.add(vid);
|
||||
|
||||
if (downloading) {
|
||||
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
mainActivity.startDownloadService(vid.getTitle());
|
||||
startDownload(downloadQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCardClick(CardView card) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String viewIdName;
|
||||
try {
|
||||
viewIdName = v.getTag().toString();
|
||||
} catch (Exception e) {
|
||||
viewIdName = "";
|
||||
}
|
||||
|
||||
if (!viewIdName.isEmpty()) {
|
||||
if (viewIdName.contains("mp3") || viewIdName.contains("mp4")) {
|
||||
Log.e(TAG, viewIdName);
|
||||
String[] buttonData = viewIdName.split("##");
|
||||
if (buttonData[0].equals("ALL")) {
|
||||
for (int i = 0; i < resultObjects.size(); i++) {
|
||||
Video vid = findVideo(resultObjects.get(i).getVideoId());
|
||||
vid.setDownloadedType(buttonData[1]);
|
||||
downloadQueue.add(vid);
|
||||
}
|
||||
|
||||
if (downloading) {
|
||||
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
mainActivity.startDownloadService(downloadQueue.peek().getTitle());
|
||||
startDownload(downloadQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,26 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
|
||||
|
||||
public class MainActivity extends AppCompatActivity{
|
||||
|
||||
ActivityMainBinding binding;
|
||||
Context context;
|
||||
|
||||
private static final String TAG = "MainActivity";
|
||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private HomeFragment homeFragment;
|
||||
private HistoryFragment historyFragment;
|
||||
|
|
@ -24,6 +28,24 @@ public class MainActivity extends AppCompatActivity{
|
|||
private Fragment lastFragment;
|
||||
private FragmentManager fm;
|
||||
|
||||
private boolean isDownloadServiceRunning = false;
|
||||
public DownloaderService downloaderService;
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
downloaderService = ((DownloaderService.LocalBinder) service).getService();
|
||||
isDownloadServiceRunning = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
downloaderService = null;
|
||||
isDownloadServiceRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
|
|
@ -32,6 +54,8 @@ public class MainActivity extends AppCompatActivity{
|
|||
setContentView(R.layout.activity_main);
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
context = getBaseContext();
|
||||
|
||||
fm = getSupportFragmentManager();
|
||||
|
||||
homeFragment = new HomeFragment();
|
||||
|
|
@ -107,15 +131,23 @@ public class MainActivity extends AppCompatActivity{
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
compositeDisposable.dispose();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void replaceFragment(Fragment f){
|
||||
fm.beginTransaction().hide(lastFragment).show(f).commit();
|
||||
lastFragment = f;
|
||||
}
|
||||
|
||||
public void startDownloadService(String title){
|
||||
if(isDownloadServiceRunning) return;
|
||||
Intent serviceIntent = new Intent(context, DownloaderService.class);
|
||||
serviceIntent.putExtra("title", title);
|
||||
context.getApplicationContext().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
public void stopDownloadService(){
|
||||
if(!isDownloadServiceRunning) return;
|
||||
context.getApplicationContext().unbindService(serviceConnection);
|
||||
isDownloadServiceRunning = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
75
app/src/main/java/com/deniscerri/ytdl/NotificationUtil.java
Normal file
75
app/src/main/java/com/deniscerri/ytdl/NotificationUtil.java
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package com.deniscerri.ytdl;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
public class NotificationUtil {
|
||||
Context context;
|
||||
public static final String DOWNLOAD_SERVICE_CHANNEL_ID = "1";
|
||||
public static final int DOWNLOAD_NOTIFICATION_ID = 1;
|
||||
public static NotificationCompat.Builder notificationBuilder;
|
||||
private static int PROGRESS_MAX = 100;
|
||||
private static int PROGRESS_CURR = 0;
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
public NotificationUtil(Context context){
|
||||
this.context = context;
|
||||
notificationBuilder = new NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID);
|
||||
notificationManager = context.getSystemService(NotificationManager.class);
|
||||
}
|
||||
|
||||
public void createNotificationChannel(){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = context.getString(R.string.download_notification_channel_name);
|
||||
String description = context.getString(R.string.download_notification_channel_description);
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
NotificationChannel channel = new NotificationChannel(DOWNLOAD_SERVICE_CHANNEL_ID, name, importance);
|
||||
channel.setDescription(description);
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public Notification createDownloadServiceNotification(PendingIntent pendingIntent, String title){
|
||||
Notification notification = notificationBuilder
|
||||
.setContentTitle(title)
|
||||
.setOngoing(true)
|
||||
.setCategory(Notification.CATEGORY_PROGRESS)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), android.R.drawable.stat_sys_download))
|
||||
.setContentText("")
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.build();
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
public void updateDownloadNotification(int id, String desc, int progress, int queue, String title){
|
||||
String contentText = "";
|
||||
if (queue > 0) contentText += queue + " items left\n";
|
||||
contentText += desc.replaceAll("\\[.*?\\]", "");
|
||||
|
||||
notificationBuilder.setProgress(100, (int) progress, false)
|
||||
.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText));
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public void cancelNotification(int id){
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package com.deniscerri.ytdl.adapter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
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.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.deniscerri.ytdl.R;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HistoryRecyclerViewAdapter extends RecyclerView.Adapter<HistoryRecyclerViewAdapter.ViewHolder> {
|
||||
private ArrayList<Video> videoList;
|
||||
private final OnItemClickListener onItemClickListener;
|
||||
private Context context;
|
||||
|
||||
public HistoryRecyclerViewAdapter(ArrayList<Video> videos, OnItemClickListener onItemClickListener, Context context){
|
||||
this.videoList = videos;
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private CardView cardView;
|
||||
|
||||
public ViewHolder(@NonNull View itemView, OnItemClickListener onItemClickListener) {
|
||||
super(itemView);
|
||||
cardView = itemView.findViewById(R.id.history_card_view);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View cardView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.history_card, parent, false);
|
||||
|
||||
return new HistoryRecyclerViewAdapter.ViewHolder(cardView, onItemClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Video video = videoList.get(position);
|
||||
CardView card = holder.cardView;
|
||||
// THUMBNAIL ----------------------------------
|
||||
ImageView thumbnail = card.findViewById(R.id.history_image_view);
|
||||
String imageURL= video.getThumb();
|
||||
|
||||
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 = card.findViewById(R.id.history_title);
|
||||
String title = video.getTitle();
|
||||
|
||||
if(title.length() > 100){
|
||||
title = title.substring(0, 40) + "...";
|
||||
}
|
||||
videoTitle.setText(title);
|
||||
|
||||
// Bottom Info ----------------------------------
|
||||
TextView bottomInfo = card.findViewById(R.id.history_info_bottom);
|
||||
String info = video.getAuthor() + " • " + video.getDuration();
|
||||
bottomInfo.setText(info);
|
||||
|
||||
// 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")){
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_music));
|
||||
}else{
|
||||
btn.setBackground(ContextCompat.getDrawable(context, R.drawable.ic_video));
|
||||
}
|
||||
|
||||
card.setOnClickListener(view -> onItemClickListener.onCardClick(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return videoList.size();
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onCardClick(int position);
|
||||
}
|
||||
|
||||
public void setVideoList(ArrayList<Video> videoList){
|
||||
this.videoList = videoList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
int size = videoList.size();
|
||||
videoList.clear();
|
||||
notifyItemRangeRemoved(0, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
package com.deniscerri.ytdl.adapter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.deniscerri.ytdl.R;
|
||||
import com.deniscerri.ytdl.database.Video;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<HomeRecyclerViewAdapter.ViewHolder> {
|
||||
private ArrayList<Video> videoList;
|
||||
private final OnItemClickListener onItemClickListener;
|
||||
private Activity activity;
|
||||
|
||||
public HomeRecyclerViewAdapter(ArrayList<Video> videos, OnItemClickListener onItemClickListener, Activity activity){
|
||||
this.videoList = videos;
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private CardView cardView;
|
||||
|
||||
public ViewHolder(@NonNull View itemView, OnItemClickListener onItemClickListener) {
|
||||
super(itemView);
|
||||
cardView = itemView.findViewById(R.id.result_card_view);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View cardView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.result_card, parent, false);
|
||||
|
||||
return new HomeRecyclerViewAdapter.ViewHolder(cardView, onItemClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Video video = videoList.get(position);
|
||||
|
||||
CardView card = holder.cardView;
|
||||
// THUMBNAIL ----------------------------------
|
||||
ImageView thumbnail = card.findViewById(R.id.result_image_view);
|
||||
String imageURL= video.getThumb();
|
||||
|
||||
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 = card.findViewById(R.id.result_title);
|
||||
String title = video.getTitle();
|
||||
|
||||
if(title.length() > 100){
|
||||
title = title.substring(0, 40) + "...";
|
||||
}
|
||||
videoTitle.setText(title);
|
||||
|
||||
// Bottom Info ----------------------------------
|
||||
|
||||
TextView bottomInfo = card.findViewById(R.id.result_info_bottom);
|
||||
String info = video.getAuthor() + " • " + video.getDuration();
|
||||
bottomInfo.setText(info);
|
||||
|
||||
// BUTTONS ----------------------------------
|
||||
String videoID = video.getVideoId();
|
||||
|
||||
LinearLayout buttonLayout = card.findViewById(R.id.download_button_layout);
|
||||
|
||||
MaterialButton musicBtn = buttonLayout.findViewById(R.id.download_music);
|
||||
musicBtn.setTag(videoID + "##mp3");
|
||||
musicBtn.setOnClickListener(view -> onItemClickListener.onButtonClick(position, "mp3"));
|
||||
|
||||
MaterialButton videoBtn = buttonLayout.findViewById(R.id.download_video);
|
||||
videoBtn.setTag(videoID + "##mp4");
|
||||
videoBtn.setOnClickListener(view -> onItemClickListener.onButtonClick(position, "mp4"));
|
||||
|
||||
|
||||
// PROGRESS BAR ----------------------------------------------------
|
||||
|
||||
ProgressBar progressBar = card.findViewById(R.id.download_progress);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
progressBar.setTag(videoID + "##progress");
|
||||
|
||||
if(video.isAudioDownloaded() == 1){
|
||||
musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
|
||||
}
|
||||
if(video.isVideoDownloaded() == 1){
|
||||
videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
|
||||
}
|
||||
|
||||
card.setTag(videoID + "##card");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return videoList.size();
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onButtonClick(int position, String type);
|
||||
void onCardClick(CardView card);
|
||||
}
|
||||
|
||||
public void setVideoList(ArrayList<Video> videoList){
|
||||
this.videoList = videoList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
int size = videoList.size();
|
||||
videoList.clear();
|
||||
notifyItemRangeRemoved(0, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -248,11 +248,11 @@ public class YoutubeAPIManager {
|
|||
}
|
||||
|
||||
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;
|
||||
String url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®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);
|
||||
JSONObject contentDetails = genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key);
|
||||
|
||||
JSONArray dataArray = res.getJSONArray("items");
|
||||
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
||||
|
|
@ -312,9 +312,7 @@ public class YoutubeAPIManager {
|
|||
if(seconds.isEmpty()) seconds = "00";
|
||||
else if(Integer.parseInt(seconds) < 10) seconds = "0" + seconds;
|
||||
if(minutes.isEmpty()){
|
||||
if(hours.isEmpty()){
|
||||
minutes = "0";
|
||||
}
|
||||
minutes = "0";
|
||||
}
|
||||
duration = minutes + ":" + seconds;
|
||||
|
||||
|
|
@ -322,7 +320,6 @@ public class YoutubeAPIManager {
|
|||
if(Integer.parseInt(minutes) < 10) minutes = "0" + minutes;
|
||||
duration = hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import java.util.ArrayList;
|
|||
public class DBManager extends SQLiteOpenHelper {
|
||||
|
||||
public static final String db_name = "ytdlnis_db";
|
||||
public static final int db_version = 1;
|
||||
public static final int db_version = 2;
|
||||
public static final String results_table_name = "results";
|
||||
public static final String history_table_name = "history";
|
||||
public static final String id = "id";
|
||||
|
|
@ -68,6 +68,24 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
" WHERE downloadedAudio=1 OR downloadedVideo=1");
|
||||
}
|
||||
|
||||
public void clearHistoryItem(Video video){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.execSQL("DELETE FROM " + history_table_name + " WHERE id=" + video.getId());
|
||||
|
||||
String where = "";
|
||||
switch(video.getDownloadedType()){
|
||||
case "mp3":
|
||||
where = " SET downloadedAudio=0 WHERE downloadedAudio=1 AND videoId='" + video.getVideoId()+"'";
|
||||
break;
|
||||
case "mp4":
|
||||
where = " SET downloadedVideo=0 WHERE downloadedVideo=1 AND videoId='" + video.getVideoId()+"'";
|
||||
break;
|
||||
}
|
||||
|
||||
//remove downloaded status from results
|
||||
db.execSQL("UPDATE "+results_table_name+ where);
|
||||
}
|
||||
|
||||
public void clearResults(){
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.execSQL("DELETE FROM " + results_table_name);
|
||||
|
|
@ -104,15 +122,18 @@ public class DBManager extends SQLiteOpenHelper {
|
|||
return list;
|
||||
}
|
||||
|
||||
public ArrayList<Video> getHistory(){
|
||||
public ArrayList<Video> getHistory(String query){
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name, null);
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name
|
||||
+ " WHERE title LIKE '%"+query+"%'",
|
||||
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),
|
||||
list.add(new Video(cursor.getInt(0),
|
||||
cursor.getString(1),
|
||||
cursor.getString(2),
|
||||
cursor.getString(3),
|
||||
cursor.getString(4),
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@ public class Video {
|
|||
}
|
||||
|
||||
//HISTORY OBJECT
|
||||
public Video(String videoId, String title, String author, String duration, String thumb,
|
||||
public Video(int id, String videoId, String title, String author, String duration, String thumb,
|
||||
String downloadedType, String downloadedTime, int isPlaylistItem) {
|
||||
this.id = id;
|
||||
this.videoId = videoId;
|
||||
this.title = title;
|
||||
this.author = author;
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
package com.deniscerri.ytdl.placeholder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper class for providing sample content for user interfaces created by
|
||||
* Android template wizards.
|
||||
* <p>
|
||||
*/
|
||||
public class PlaceholderContent {
|
||||
|
||||
/**
|
||||
* An array of sample (placeholder) items.
|
||||
*/
|
||||
public static final List<PlaceholderItem> ITEMS = new ArrayList<PlaceholderItem>();
|
||||
|
||||
/**
|
||||
* A map of sample (placeholder) items, by ID.
|
||||
*/
|
||||
public static final Map<String, PlaceholderItem> ITEM_MAP = new HashMap<String, PlaceholderItem>();
|
||||
|
||||
private static final int COUNT = 25;
|
||||
|
||||
static {
|
||||
// Add some sample items.
|
||||
for (int i = 1; i <= COUNT; i++) {
|
||||
addItem(createPlaceholderItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addItem(PlaceholderItem item) {
|
||||
ITEMS.add(item);
|
||||
ITEM_MAP.put(item.id, item);
|
||||
}
|
||||
|
||||
private static PlaceholderItem createPlaceholderItem(int position) {
|
||||
return new PlaceholderItem(String.valueOf(position), "Item " + position, makeDetails(position));
|
||||
}
|
||||
|
||||
private static String makeDetails(int position) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Details about Item: ").append(position);
|
||||
for (int i = 0; i < position; i++) {
|
||||
builder.append("\nMore details information here.");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A placeholder item representing a piece of content.
|
||||
*/
|
||||
public static class PlaceholderItem {
|
||||
public final String id;
|
||||
public final String content;
|
||||
public final String details;
|
||||
|
||||
public PlaceholderItem(String id, String content, String details) {
|
||||
this.id = id;
|
||||
this.content = content;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,9h8v10L8,19L8,9zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_open_in_new_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_open_in_new_24.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="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_delete_all.xml
Normal file
5
app/src/main/res/drawable/ic_delete_all.xml
Normal file
|
|
@ -0,0 +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="?android:colorPrimary" android:pathData="M15,16h4v2h-4zM15,8h7v2h-7zM15,12h6v2h-6zM3,18c0,1.1 0.9,2 2,2h6c1.1,0 2,-0.9 2,-2L13,8L3,8v10zM14,5h-3l-1,-1L6,4L5,5L2,5v2h12z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_link.xml
Normal file
5
app/src/main/res/drawable/ic_link.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="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
|
||||
</vector>
|
||||
54
app/src/main/res/drawable/ic_no_results.xml
Normal file
54
app/src/main/res/drawable/ic_no_results.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="254dp"
|
||||
android:height="129dp"
|
||||
android:viewportWidth="254"
|
||||
android:viewportHeight="129">
|
||||
<path
|
||||
android:pathData="M63.4,35c0,1.9 0.7,2 16.5,2 14.8,-0 16.3,0.2 14.4,1.5 -2.8,1.9 -3.7,5.5 -1.4,5.5 0.9,-0 2.4,-0.9 3.4,-2 1,-1.2 2.9,-2 4.7,-2 1.9,-0 3.4,0.7 4.2,2 1.4,2.2 4.8,2.7 4.8,0.7 0,-2.2 -2.9,-5.5 -5.5,-6.1 -1.4,-0.4 -2.5,-1.3 -2.5,-2.1 0,-1.3 -2.9,-1.5 -19.3,-1.5 -18.6,-0 -19.3,0.1 -19.3,2z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M163,35c0,1.9 0.7,2 16.5,2l16.4,-0 -2.4,2.5c-1.4,1.3 -2.5,2.9 -2.5,3.5 0,1.8 3.9,1.1 5,-1 0.8,-1.4 2.1,-2 4.5,-2 2.4,-0 3.9,0.6 4.7,2 1.5,2.3 4.8,2.7 4.8,0.5 0,-2.1 -3.4,-5.3 -6.4,-6.1 -1.6,-0.4 -2.3,-1.1 -1.9,-2 0.4,-1.2 -2.6,-1.4 -19.1,-1.4 -18.9,-0 -19.6,0.1 -19.6,2z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M36.1,40.9c-11.3,14.2 -13.3,33.1 -5.1,49.1 3,6 7.8,12 9.6,12 1.9,-0 1.8,-1.4 -0.3,-3.1 -0.9,-0.8 -3.3,-3.9 -5.3,-7.1 -4.6,-7.1 -6.4,-15.1 -5.8,-26 0.5,-9.7 2.9,-16 8.9,-23 4.3,-5.1 4.6,-5.8 2.5,-5.8 -0.8,-0 -2.8,1.7 -4.5,3.9z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M224,38c0,0.7 1.4,2.8 3,4.7 7.6,8.6 11.1,22 9,34.1 -1.5,8.8 -3.9,14.1 -8.9,19.5 -4.4,4.8 -4.9,5.7 -2.7,5.7 2.2,-0 7.6,-6.9 11,-14 2.9,-6.1 3.1,-7.2 3.1,-18 0,-9.6 -0.4,-12.4 -2.2,-17 -3.5,-8.4 -12.3,-19.2 -12.3,-15z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M151,41.4c0,0.8 1,1.6 2.1,1.8 3,0.4 4.7,3.3 3.3,5.5 -0.6,1 -2.1,1.9 -3.3,2.1 -2.8,0.4 -2.7,3.2 0.2,3.2 1.2,-0 3.3,-0.7 4.5,-1.6 3.1,-2.1 3.1,-8.7 0,-10.8 -2.8,-2 -6.8,-2.1 -6.8,-0.2z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M98,47c0,2.7 0.3,3 3,3 2.7,-0 3,-0.3 3,-3 0,-2.7 -0.3,-3 -3,-3 -2.7,-0 -3,0.3 -3,3z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M198,47c0,2.7 0.3,3 3,3 2.7,-0 3,-0.3 3,-3 0,-2.7 -0.3,-3 -3,-3 -2.7,-0 -3,0.3 -3,3z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M124,64.7c-6.5,1.1 -21,6.9 -21,8.4 0,1.3 0.6,1.2 8.9,-1.8 6.8,-2.5 9,-2.8 20.6,-2.8 11.6,-0 13.8,0.3 20.8,2.8 8.8,3.1 8.7,3.1 8.7,1.6 0,-1.5 -10.5,-5.9 -18,-7.5 -6.2,-1.3 -14.6,-1.6 -20,-0.7z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M43.2,77.5c2.2,2.9 4.1,4.4 5.6,4.5 2.7,-0 2.7,-0.1 0.2,-5 -1.8,-3.5 -2.5,-4 -5.6,-4l-3.5,-0 3.3,4.5z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M54.7,77.5c2.4,2.9 4.5,4.5 6,4.5l2.3,-0 -2.2,-4.5c-2,-4.2 -2.5,-4.5 -6,-4.5l-3.9,-0 3.8,4.5z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M201.7,77.5c2.4,2.9 4.5,4.5 6,4.5l2.2,-0 -2.2,-4.5c-2,-4.2 -2.5,-4.5 -6,-4.5l-3.8,-0 3.8,4.5z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
<path
|
||||
android:pathData="M213.1,77.4c2.2,3.2 4,4.6 5.5,4.6l2.3,-0 -1.9,-4.3c-1.7,-3.7 -2.3,-4.2 -5.6,-4.5l-3.6,-0.3 3.3,4.5z"
|
||||
android:fillColor="?android:textColorPrimary"
|
||||
android:strokeColor="?android:textColorPrimary"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_refresh.xml
Normal file
5
app/src/main/res/drawable/ic_refresh.xml
Normal file
|
|
@ -0,0 +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="?android:colorPrimary" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
||||
app:menu="@menu/bottom_nav_menu"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -6,116 +6,105 @@
|
|||
android:id="@+id/historycoordinator"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/history_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/history_big_linearlayout"
|
||||
android:layout_marginTop="?attr/actionBarSize">
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swiperefresh"
|
||||
<LinearLayout
|
||||
android:id="@+id/home_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="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"
|
||||
<LinearLayout
|
||||
android:id="@+id/historylinearLayout1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/recycler_view_history"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:layout_margin="10dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
>
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/shimmer_history_framelayout"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/history_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:id="@+id/shimmer_history_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<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_shimmer" />
|
||||
|
||||
<include layout="@layout/history_card"
|
||||
android:visibility="gone" />
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/history_card_shimmer" />
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/shimmer_history_framelayout"
|
||||
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" />
|
||||
|
||||
|
||||
<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>
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
</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"/>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/history_appbarlayout"
|
||||
app:liftOnScroll="false"
|
||||
android:background="@android:color/transparent"
|
||||
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">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/history_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:layout_scrollFlags="scroll|enterAlways|snap"
|
||||
app:title="@string/history"
|
||||
app:menu="@menu/history_menu"
|
||||
/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<include layout="@layout/history_bottom_sheet"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -6,104 +6,75 @@
|
|||
android:id="@+id/homecoordinator"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/home_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/home_big_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
>
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swiperefreshhome"
|
||||
<LinearLayout
|
||||
android:id="@+id/home_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:visibility="visible"
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:backgroundTintMode="src_over"
|
||||
tools:context=".HomeFragment"
|
||||
android:id="@+id/scrollView1"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/recycler_view_home"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:layout_margin="10dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
>
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<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/home_main_linearlayout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
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" />
|
||||
|
||||
<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/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/download_all_card"
|
||||
android:visibility="gone" />
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<include layout="@layout/result_card"
|
||||
android:visibility="gone" />
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/result_card_shimmer" />
|
||||
|
||||
<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>
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<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"/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/home_appbarlayout"
|
||||
app:liftOnScroll="false"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
|
|
|||
80
app/src/main/res/layout/history_bottom_sheet.xml
Normal file
80
app/src/main/res/layout/history_bottom_sheet.xml
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout android:id="@+id/history_element_bottom_sheet"
|
||||
style="@style/Widget.Material3.BottomSheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:textSize="25sp"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_author"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingTop="5dp"
|
||||
android:textSize="15sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||
android:id="@+id/bottom_sheet_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp"
|
||||
android:layout_gravity="center"
|
||||
app:icon="@drawable/ic_link"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="right"
|
||||
android:layout_margin="20dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
|
||||
android:id="@+id/bottomsheet_remove_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="10dp"
|
||||
app:icon="@drawable/ic_baseline_delete_outline_24"
|
||||
android:text="@string/Remove" />
|
||||
|
||||
<Button
|
||||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||
android:id="@+id/bottomsheet_open_link_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/Open_Link"
|
||||
app:icon="@drawable/ic_baseline_open_in_new_24"
|
||||
android:autoLink="all"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</FrameLayout>
|
||||
33
app/src/main/res/layout/history_no_results.xml
Normal file
33
app/src/main/res/layout/history_no_results.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:layout_margin="100dp"
|
||||
tools:ignore="MissingConstraints">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/ic_no_results" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/no_results"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
@ -3,12 +3,25 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/search_history"
|
||||
android:title="@string/search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:showAsAction="collapseActionView|always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_history"
|
||||
android:icon="@drawable/ic_refresh"
|
||||
android:title="@string/refresh_history"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_history"
|
||||
android:orderInCategory="100"
|
||||
android:icon="@drawable/ic_delete_all"
|
||||
android:title="@string/fshi_historin"
|
||||
app:showAsAction="never" />
|
||||
|
||||
|
||||
</menu>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@
|
|||
app:showAsAction="collapseActionView|always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_results"
|
||||
android:icon="@drawable/ic_refresh"
|
||||
android:title="@string/refresh_results"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_results"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/fshi_rezultatet"
|
||||
app:showAsAction="never" />
|
||||
|
||||
|
|
|
|||
|
|
@ -32,4 +32,14 @@
|
|||
<string name="added_to_queue">U vendos në rradhë!</string>
|
||||
<string name="ytdl_already_updating">Duke u përditësuar. Prisni!</string>
|
||||
<string name="ytdl_updating_started">Filloi Perditesimi</string>
|
||||
<string name="Open_Link">Hap Linkun</string>
|
||||
<string name="Remove">Hiqe</string>
|
||||
<string name="refresh_results">Rifresko Rezultatet</string>
|
||||
<string name="refresh_history">Rifresko Historinë</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Anullo</string>
|
||||
<string name="confirm_delete_history_desc">Kjo do të fshijë të gjithë elementet në histori! Nuk mund t\'i rikthesh më!</string>
|
||||
<string name="confirm_delete_history">Konfirmo</string>
|
||||
<string name="search_history_hint">Kërko nga Historia</string>
|
||||
<string name="no_results">Nuk ka Rezultate</string>
|
||||
</resources>
|
||||
|
|
@ -32,4 +32,14 @@
|
|||
<string name="ytdl_update_failed">Failed Updating</string>
|
||||
<string name="ytdl_already_updating">Updating in Progress! Please Wait!</string>
|
||||
<string name="ytdl_updating_started">Updating Started</string>
|
||||
<string name="Open_Link">Open link</string>
|
||||
<string name="Remove">Remove</string>
|
||||
<string name="refresh_results">Refresh Results</string>
|
||||
<string name="refresh_history">Refresh History</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="confirm_delete_history_desc">This will delete every item on your history! You can\'t recover them!</string>
|
||||
<string name="confirm_delete_history">Confirm</string>
|
||||
<string name="search_history_hint">Search from History</string>
|
||||
<string name="no_results">No Results</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.yausername.ytdl;
|
||||
package com.deniscerri.ytdl;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ ext {
|
|||
}
|
||||
|
||||
allprojects {
|
||||
group = 'com.github.yausername'
|
||||
group = 'com.github.deniscerri'
|
||||
version = versionName
|
||||
|
||||
repositories {
|
||||
|
|
|
|||
Loading…
Reference in a new issue