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>
|
<PersistentState>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<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>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
|
|
@ -33,9 +33,10 @@
|
||||||
</option>
|
</option>
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
|
<entry key="assetSourceType" value="FILE" />
|
||||||
<entry key="color" value="00a6ff" />
|
<entry key="color" value="00a6ff" />
|
||||||
<entry key="outputName" value="ic_code" />
|
<entry key="outputName" value="ic_no_results" />
|
||||||
<entry key="sourceFile" value="C:\Users\denis\Desktop\youtubedl-android-0.13.1\youtubedl-android-0.13.1" />
|
<entry key="sourceFile" value="C:\Users\denis\Desktop\eaKdW401.svg" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,10 @@
|
||||||
<option name="name" value="maven" />
|
<option name="name" value="maven" />
|
||||||
<option name="url" value="https://jitpack.io" />
|
<option name="url" value="https://jitpack.io" />
|
||||||
</remote-repository>
|
</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>
|
</component>
|
||||||
</project>
|
</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_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_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_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_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_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_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.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_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_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_update.xml" value="0.1275" />
|
||||||
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/drawable/ic_video.xml" value="0.1275" />
|
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/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/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/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/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_history.xml" value="0.2" />
|
||||||
<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_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/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.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_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.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/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/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/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/history_menu.xml" value="0.17" />
|
||||||
<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/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/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" />
|
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/xml/root_preferences.xml" value="0.18" />
|
||||||
</map>
|
</map>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.yausername.ytdl;
|
package com.deniscerri.ytdl;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
|
|
@ -18,7 +19,6 @@
|
||||||
<activity android:name=".MainActivity">
|
<activity android:name=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
@ -28,7 +28,10 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
<service
|
||||||
|
android:name=".DownloaderService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name = "ytAPIkey"
|
android:name = "ytAPIkey"
|
||||||
android:value = "${ytAPI}"
|
android:value = "${ytAPI}"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
package com.deniscerri.ytdl;
|
package com.deniscerri.ytdl;
|
||||||
|
|
||||||
import android.app.Application;
|
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.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
|
@ -13,7 +9,6 @@ import com.google.android.material.color.DynamicColors;
|
||||||
import com.yausername.ffmpeg.FFmpeg;
|
import com.yausername.ffmpeg.FFmpeg;
|
||||||
import com.yausername.youtubedl_android.YoutubeDL;
|
import com.yausername.youtubedl_android.YoutubeDL;
|
||||||
import com.yausername.youtubedl_android.YoutubeDLException;
|
import com.yausername.youtubedl_android.YoutubeDLException;
|
||||||
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
|
@ -25,15 +20,16 @@ import io.reactivex.schedulers.Schedulers;
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|
||||||
private static final String TAG = "App";
|
private static final String TAG = "App";
|
||||||
public static final String DOWNLOAD_CHANNEL_ID = "1";
|
public static NotificationUtil notificationUtil;
|
||||||
ActivityMainBinding binding;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this);
|
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||||
createNotificationChannel();
|
|
||||||
|
notificationUtil = new NotificationUtil(this);
|
||||||
|
createNotificationChannels();
|
||||||
|
|
||||||
configureRxJavaErrorHandler();
|
configureRxJavaErrorHandler();
|
||||||
Completable.fromAction(this::initLibraries).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
|
Completable.fromAction(this::initLibraries).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -71,15 +67,7 @@ public class App extends Application {
|
||||||
FFmpeg.getInstance().init(this);
|
FFmpeg.getInstance().init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
private void createNotificationChannels() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
notificationUtil.createNotificationChannel();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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;
|
package com.deniscerri.ytdl;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
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 android.os.Bundle;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.cardview.widget.CardView;
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.TextView;
|
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.DBManager;
|
||||||
import com.deniscerri.ytdl.database.Video;
|
import com.deniscerri.ytdl.database.Video;
|
||||||
import com.facebook.shimmer.ShimmerFrameLayout;
|
import com.facebook.shimmer.ShimmerFrameLayout;
|
||||||
import com.google.android.material.appbar.MaterialToolbar;
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment representing a list of Items.
|
* 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 LinearLayout linearLayout;
|
||||||
private ScrollView scrollView;
|
|
||||||
private View fragmentView;
|
private View fragmentView;
|
||||||
private DBManager dbManager;
|
private DBManager dbManager;
|
||||||
Context context;
|
Context context;
|
||||||
private LayoutInflater layoutinflater;
|
private LayoutInflater layoutinflater;
|
||||||
private ShimmerFrameLayout shimmerCards;
|
private ShimmerFrameLayout shimmerCards;
|
||||||
private MaterialToolbar topAppBar;
|
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 TAG = "HistoryFragment";
|
||||||
|
private static final String youtubeVideoURL = "https://www.youtube.com/watch?v=";
|
||||||
|
|
||||||
public HistoryFragment() {
|
public HistoryFragment() {
|
||||||
}
|
}
|
||||||
|
|
@ -75,31 +80,23 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_history, container, false);
|
fragmentView = inflater.inflate(R.layout.fragment_history, container, false);
|
||||||
context = fragmentView.getContext();
|
context = fragmentView.getContext();
|
||||||
layoutinflater = LayoutInflater.from(context);
|
layoutinflater = LayoutInflater.from(context);
|
||||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
linearLayout = fragmentView.findViewById(R.id.historylinearLayout1);
|
||||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
|
||||||
shimmerCards = fragmentView.findViewById(R.id.shimmer_history_framelayout);
|
shimmerCards = fragmentView.findViewById(R.id.shimmer_history_framelayout);
|
||||||
topAppBar = fragmentView.findViewById(R.id.history_toolbar);
|
topAppBar = fragmentView.findViewById(R.id.history_toolbar);
|
||||||
|
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
|
historyObjects = new ArrayList<>();
|
||||||
|
|
||||||
SwipeRefreshLayout swipeRefreshLayout = fragmentView.findViewById(R.id.swiperefresh);
|
recyclerView = fragmentView.findViewById(R.id.recycler_view_history);
|
||||||
swipeRefreshLayout.setOnRefreshListener(() -> {
|
|
||||||
initCards();
|
historyRecyclerViewAdapter = new HistoryRecyclerViewAdapter(historyObjects, this, context);
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
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();
|
initMenu();
|
||||||
initCards();
|
initCards();
|
||||||
return fragmentView;
|
return fragmentView;
|
||||||
|
|
@ -109,24 +106,26 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||||
private void initCards(){
|
private void initCards(){
|
||||||
shimmerCards.startShimmer();
|
shimmerCards.startShimmer();
|
||||||
shimmerCards.setVisibility(View.VISIBLE);
|
shimmerCards.setVisibility(View.VISIBLE);
|
||||||
|
historyRecyclerViewAdapter.clear();
|
||||||
linearLayout.removeAllViews();
|
linearLayout.removeAllViews();
|
||||||
try{
|
try{
|
||||||
Thread thread = new Thread(() -> {
|
Thread thread = new Thread(() -> {
|
||||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||||
ArrayList<Video> historyObjects = dbManager.getHistory();
|
historyObjects = dbManager.getHistory("");
|
||||||
for(int i = historyObjects.size()-1; i >= 0; i--){
|
|
||||||
createCard(historyObjects.get(i));
|
|
||||||
}
|
|
||||||
TextView padding = new TextView(context);
|
|
||||||
int dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, context.getResources().getDisplayMetrics());
|
|
||||||
padding.setHeight(dp);
|
|
||||||
padding.setGravity(Gravity.CENTER);
|
|
||||||
|
|
||||||
uiHandler.post(() -> {
|
uiHandler.post(() -> {
|
||||||
linearLayout.addView(padding);
|
historyRecyclerViewAdapter.setVideoList(historyObjects);
|
||||||
shimmerCards.stopShimmer();
|
shimmerCards.stopShimmer();
|
||||||
shimmerCards.setVisibility(View.GONE);
|
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();
|
thread.start();
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
|
@ -136,63 +135,87 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollToTop(){
|
public void scrollToTop(){
|
||||||
scrollView.smoothScrollTo(0,0);
|
recyclerView.smoothScrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCard(Video video){
|
private void addNoResultsView(){
|
||||||
RelativeLayout r = new RelativeLayout(context);
|
RelativeLayout no_results = new RelativeLayout(context);
|
||||||
layoutinflater.inflate(R.layout.history_card, r);
|
layoutinflater.inflate(R.layout.history_no_results, no_results);
|
||||||
|
linearLayout.addView(no_results);
|
||||||
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 initMenu(){
|
private void initMenu(){
|
||||||
topAppBar.setOnClickListener(view -> {
|
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||||
scrollToTop();
|
@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) -> {
|
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
||||||
switch (m.getItemId()){
|
switch (m.getItemId()){
|
||||||
case R.id.delete_history:
|
case R.id.delete_history:
|
||||||
dbManager.clearHistory();
|
if(historyObjects.size() == 0){
|
||||||
linearLayout.removeAllViews();
|
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;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -202,9 +225,86 @@ public class HistoryFragment extends Fragment implements View.OnClickListener{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (v.getId() == R.id.fab_history) {
|
int id = v.getId();
|
||||||
scrollView.smoothScrollBy(0, 0);
|
switch (id){
|
||||||
scrollView.smoothScrollTo(0, 0);
|
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.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -37,17 +32,17 @@ import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.widget.NestedScrollView;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
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.api.YoutubeAPIManager;
|
||||||
import com.deniscerri.ytdl.database.DBManager;
|
import com.deniscerri.ytdl.database.DBManager;
|
||||||
import com.deniscerri.ytdl.database.Video;
|
import com.deniscerri.ytdl.database.Video;
|
||||||
import com.facebook.shimmer.ShimmerFrameLayout;
|
import com.facebook.shimmer.ShimmerFrameLayout;
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
import com.google.android.material.appbar.MaterialToolbar;
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
import com.google.android.material.button.MaterialButton;
|
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.DownloadProgressCallback;
|
||||||
import com.yausername.youtubedl_android.YoutubeDL;
|
import com.yausername.youtubedl_android.YoutubeDL;
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest;
|
import com.yausername.youtubedl_android.YoutubeDLRequest;
|
||||||
|
|
@ -70,15 +65,18 @@ import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
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 boolean downloading = false;
|
||||||
private View fragmentView;
|
private View fragmentView;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private String inputQuery;
|
private String inputQuery;
|
||||||
private LinearLayout linearLayout;
|
private LinearLayout homeLinearLayout;
|
||||||
private ScrollView scrollView;
|
private RecyclerView recyclerView;
|
||||||
|
private HomeRecyclerViewAdapter homeRecyclerViewAdapter;
|
||||||
|
private NestedScrollView scrollView;
|
||||||
private ShimmerFrameLayout shimmerCards;
|
private ShimmerFrameLayout shimmerCards;
|
||||||
private LayoutInflater layoutinflater;
|
private LayoutInflater layoutinflater;
|
||||||
|
|
||||||
Context context;
|
Context context;
|
||||||
Activity activity;
|
Activity activity;
|
||||||
private MaterialToolbar topAppBar;
|
private MaterialToolbar topAppBar;
|
||||||
|
|
@ -86,33 +84,22 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
private static final String TAG = "HomeFragment";
|
private static final String TAG = "HomeFragment";
|
||||||
|
|
||||||
private ArrayList<Video> resultObjects;
|
private ArrayList<Video> resultObjects;
|
||||||
|
|
||||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||||
|
|
||||||
private DBManager dbManager;
|
private DBManager dbManager;
|
||||||
private YoutubeAPIManager youtubeAPIManager;
|
private YoutubeAPIManager youtubeAPIManager;
|
||||||
private NotificationCompat.Builder download_notification;
|
|
||||||
private NotificationManagerCompat notificationManager;
|
|
||||||
private Queue<Video> downloadQueue;
|
private Queue<Video> downloadQueue;
|
||||||
|
|
||||||
|
MainActivity mainActivity;
|
||||||
|
private static final NotificationUtil notificationUtil = App.notificationUtil;
|
||||||
|
|
||||||
private final DownloadProgressCallback callback = new DownloadProgressCallback() {
|
private final DownloadProgressCallback callback = new DownloadProgressCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgressUpdate(float progress, long etaInSeconds, String line) {
|
public void onProgressUpdate(float progress, long etaInSeconds, String line) {
|
||||||
activity.runOnUiThread(() -> {
|
activity.runOnUiThread(() -> {
|
||||||
progressBar.setProgress((int) progress);
|
progressBar.setProgress((int) progress);
|
||||||
String contentText = "";
|
notificationUtil.updateDownloadNotification(NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
|
||||||
|
line, (int) progress, downloadQueue.size(), downloadQueue.peek().getTitle());
|
||||||
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());
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -129,81 +116,51 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
setHasOptionsMenu(true);
|
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
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
|
||||||
compositeDisposable = new CompositeDisposable();
|
compositeDisposable = new CompositeDisposable();
|
||||||
|
downloadQueue = new LinkedList<>();
|
||||||
|
resultObjects = new ArrayList<>();
|
||||||
|
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_home, container, false);
|
fragmentView = inflater.inflate(R.layout.fragment_home, container, false);
|
||||||
context = fragmentView.getContext();
|
context = fragmentView.getContext();
|
||||||
activity = getActivity();
|
activity = getActivity();
|
||||||
|
mainActivity = (MainActivity) activity;
|
||||||
layoutinflater = LayoutInflater.from(context);
|
layoutinflater = LayoutInflater.from(context);
|
||||||
|
|
||||||
|
|
||||||
//initViews
|
//initViews
|
||||||
linearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
homeLinearLayout = fragmentView.findViewById(R.id.linearLayout1);
|
||||||
scrollView = fragmentView.findViewById(R.id.scrollView1);
|
|
||||||
shimmerCards = fragmentView.findViewById(R.id.shimmer_results_framelayout);
|
shimmerCards = fragmentView.findViewById(R.id.shimmer_results_framelayout);
|
||||||
topAppBar = fragmentView.findViewById(R.id.home_toolbar);
|
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<>();
|
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(resultObjects, this, activity);
|
||||||
|
recyclerView.setAdapter(homeRecyclerViewAdapter);
|
||||||
SwipeRefreshLayout swipeRefreshLayout = fragmentView.findViewById(R.id.swiperefreshhome);
|
recyclerView.setNestedScrollingEnabled(false);
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
initMenu();
|
initMenu();
|
||||||
|
|
||||||
if(inputQuery != null){
|
if (inputQuery != null) {
|
||||||
parseQuery();
|
parseQuery();
|
||||||
inputQuery = null;
|
inputQuery = null;
|
||||||
}else{
|
} else {
|
||||||
initCards();
|
initCards();
|
||||||
}
|
}
|
||||||
|
|
||||||
return fragmentView;
|
return fragmentView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void initCards() {
|
||||||
private void initCards(){
|
|
||||||
shimmerCards.startShimmer();
|
shimmerCards.startShimmer();
|
||||||
shimmerCards.setVisibility(View.VISIBLE);
|
shimmerCards.setVisibility(View.VISIBLE);
|
||||||
linearLayout.removeAllViews();
|
homeRecyclerViewAdapter.clear();
|
||||||
try{
|
homeLinearLayout.removeAllViews();
|
||||||
|
try {
|
||||||
Thread thread = new Thread(() -> {
|
Thread thread = new Thread(() -> {
|
||||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
|
|
@ -211,51 +168,40 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
resultObjects = dbManager.getResults();
|
resultObjects = dbManager.getResults();
|
||||||
boolean trending = false;
|
boolean trending = false;
|
||||||
|
|
||||||
if(resultObjects.size() == 0){
|
if (resultObjects.size() == 0) {
|
||||||
trending = true;
|
trending = true;
|
||||||
try {
|
try {
|
||||||
resultObjects = youtubeAPIManager.getTrending(context);
|
resultObjects = youtubeAPIManager.getTrending(context);
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (resultObjects != null) {
|
||||||
TextView trendingText = new TextView(context);
|
|
||||||
|
|
||||||
if(resultObjects != null){
|
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
|
|
||||||
if(trending){
|
if (trending) {
|
||||||
trendingText.setText(R.string.Trending);
|
uiHandler.post(this::addTrendingText);
|
||||||
trendingText.setPadding(30, 0 ,0 ,0);
|
|
||||||
uiHandler.post(() -> linearLayout.addView(trendingText));
|
|
||||||
|
|
||||||
}else{
|
} else {
|
||||||
if(resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1){
|
if (resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1) {
|
||||||
createDownloadAllCard();
|
createDownloadAllCard();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < resultObjects.size(); i++){
|
|
||||||
createCard(resultObjects.get(i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.post(() -> {
|
uiHandler.post(() -> {
|
||||||
|
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||||
shimmerCards.stopShimmer();
|
shimmerCards.stopShimmer();
|
||||||
shimmerCards.setVisibility(View.GONE);
|
shimmerCards.setVisibility(View.GONE);
|
||||||
if(resultObjects.size() > 1){
|
|
||||||
addEndofResultsText();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMenu(){
|
private void initMenu() {
|
||||||
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
|
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
||||||
|
|
@ -290,31 +236,36 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
topAppBar.setOnClickListener(view -> scrollToTop());
|
topAppBar.setOnClickListener(view -> scrollToTop());
|
||||||
|
|
||||||
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
|
||||||
if (m.getItemId() == R.id.delete_results) {
|
switch (m.getItemId()){
|
||||||
dbManager.clearResults();
|
case R.id.delete_results:
|
||||||
linearLayout.removeAllViews();
|
dbManager.clearResults();
|
||||||
initCards();
|
recyclerView.removeAllViews();
|
||||||
return true;
|
initCards();
|
||||||
|
return true;
|
||||||
|
case R.id.refresh_results:
|
||||||
|
recyclerView.removeAllViews();
|
||||||
|
initCards();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleIntent(Intent intent){
|
public void handleIntent(Intent intent) {
|
||||||
inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT);
|
inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollToTop(){
|
public void scrollToTop() {
|
||||||
scrollView.smoothScrollTo(0,0);
|
scrollView.smoothScrollTo(-100,-100);
|
||||||
|
new Handler(Looper.getMainLooper()).post(() -> ((AppBarLayout) topAppBar.getParent()).setExpanded(true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseQuery() {
|
private void parseQuery() {
|
||||||
shimmerCards.startShimmer();
|
shimmerCards.startShimmer();
|
||||||
shimmerCards.setVisibility(View.VISIBLE);
|
shimmerCards.setVisibility(View.VISIBLE);
|
||||||
linearLayout.removeAllViews();
|
homeRecyclerViewAdapter.clear();
|
||||||
|
homeLinearLayout.removeAllViews();
|
||||||
|
|
||||||
resultObjects = new ArrayList<>();
|
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
youtubeAPIManager = new YoutubeAPIManager(context);
|
youtubeAPIManager = new YoutubeAPIManager(context);
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
|
|
@ -323,38 +274,37 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
Pattern p = Pattern.compile("^(https?)://(www.)?youtu(.be)?");
|
Pattern p = Pattern.compile("^(https?)://(www.)?youtu(.be)?");
|
||||||
Matcher m = p.matcher(inputQuery);
|
Matcher m = p.matcher(inputQuery);
|
||||||
|
|
||||||
if(m.find()){
|
if (m.find()) {
|
||||||
type = "Video";
|
type = "Video";
|
||||||
if (inputQuery.contains("playlist?list=")) {
|
if (inputQuery.contains("playlist?list=")) {
|
||||||
type = "Playlist";
|
type = "Playlist";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.e(TAG, inputQuery + " "+ type);
|
Log.e(TAG, inputQuery + " " + type);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "Search": {
|
case "Search": {
|
||||||
Thread thread = new Thread(new Runnable(){
|
Thread thread = new Thread(new Runnable() {
|
||||||
private final String query;
|
private final String query;
|
||||||
|
|
||||||
{
|
{
|
||||||
this.query = inputQuery;
|
this.query = inputQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(){
|
public void run() {
|
||||||
try {
|
try {
|
||||||
resultObjects = youtubeAPIManager.search(query);
|
resultObjects = youtubeAPIManager.search(query);
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
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);
|
dbManager.addToResults(resultObjects);
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
addEndofResultsText();
|
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||||
|
//addEndofResultsText();
|
||||||
shimmerCards.stopShimmer();
|
shimmerCards.stopShimmer();
|
||||||
shimmerCards.setVisibility(View.GONE);
|
shimmerCards.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
|
|
@ -362,36 +312,40 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
break;
|
break;
|
||||||
}case "Video": {
|
}
|
||||||
|
case "Video": {
|
||||||
String[] el = inputQuery.split("/");
|
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);
|
inputQuery = inputQuery.substring(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inputQuery.contains("&list=")){
|
if (inputQuery.contains("&list=")) {
|
||||||
el = inputQuery.split("&list=");
|
el = inputQuery.split("&list=");
|
||||||
inputQuery = el[0];
|
inputQuery = el[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread thread = new Thread(new Runnable(){
|
Thread thread = new Thread(new Runnable() {
|
||||||
private final String query;
|
private final String query;
|
||||||
|
|
||||||
{
|
{
|
||||||
this.query = inputQuery;
|
this.query = inputQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(){
|
public void run() {
|
||||||
try {
|
try {
|
||||||
resultObjects.add(youtubeAPIManager.getVideo(query));
|
resultObjects.add(youtubeAPIManager.getVideo(query));
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
createCard(resultObjects.get(0));
|
|
||||||
dbManager.addToResults(resultObjects);
|
dbManager.addToResults(resultObjects);
|
||||||
|
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
|
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||||
shimmerCards.stopShimmer();
|
shimmerCards.stopShimmer();
|
||||||
shimmerCards.setVisibility(View.GONE);
|
shimmerCards.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
|
|
@ -399,31 +353,32 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
break;
|
break;
|
||||||
}case "Playlist": {
|
}
|
||||||
|
case "Playlist": {
|
||||||
inputQuery = inputQuery.split("list=")[1];
|
inputQuery = inputQuery.split("list=")[1];
|
||||||
Thread thread = new Thread(new Runnable() {
|
Thread thread = new Thread(new Runnable() {
|
||||||
private final String query;
|
private final String query;
|
||||||
|
|
||||||
{
|
{
|
||||||
this.query = inputQuery;
|
this.query = inputQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(){
|
public void run() {
|
||||||
try {
|
try {
|
||||||
resultObjects = youtubeAPIManager.getPlaylist(query, "");
|
resultObjects = youtubeAPIManager.getPlaylist(query, "");
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
dbManager.addToResults(resultObjects);
|
dbManager.addToResults(resultObjects);
|
||||||
// DOWNLOAD ALL BUTTON
|
// DOWNLOAD ALL BUTTON
|
||||||
if(resultObjects.size() > 1){
|
if (resultObjects.size() > 1) {
|
||||||
createDownloadAllCard();
|
createDownloadAllCard();
|
||||||
}
|
}
|
||||||
for(int i = 0 ; i < resultObjects.size(); i++){
|
|
||||||
createCard(resultObjects.get(i));
|
|
||||||
}
|
|
||||||
new Handler(Looper.getMainLooper()).post(() -> {
|
new Handler(Looper.getMainLooper()).post(() -> {
|
||||||
addEndofResultsText();
|
homeRecyclerViewAdapter.setVideoList(resultObjects);
|
||||||
|
//addEndofResultsText();
|
||||||
shimmerCards.stopShimmer();
|
shimmerCards.stopShimmer();
|
||||||
shimmerCards.setVisibility(View.GONE);
|
shimmerCards.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
|
|
@ -433,23 +388,20 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEndofResultsText(){
|
private void addTrendingText(){
|
||||||
TextView padding = new TextView(context);
|
TextView trendingText = new TextView(context);
|
||||||
int dp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, fragmentView.getResources().getDisplayMetrics());
|
trendingText.setText(R.string.Trending);
|
||||||
padding.setHeight(dp);
|
trendingText.setPadding(70, 0 , 0, 0);
|
||||||
padding.setText(R.string.end_of_results);
|
homeLinearLayout.addView(trendingText);
|
||||||
padding.setGravity(Gravity.CENTER);
|
|
||||||
linearLayout.addView(padding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDownloadAllCard(){
|
private void createDownloadAllCard() {
|
||||||
RelativeLayout r = new RelativeLayout(context);
|
RelativeLayout r = new RelativeLayout(context);
|
||||||
layoutinflater.inflate(R.layout.download_all_card, r);
|
layoutinflater.inflate(R.layout.download_all_card, r);
|
||||||
|
|
||||||
|
|
@ -466,116 +418,13 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
videoBtn.setOnClickListener(this);
|
videoBtn.setOnClickListener(this);
|
||||||
|
|
||||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||||
uiHandler.post(() -> linearLayout.addView(r));
|
uiHandler.post(() -> homeLinearLayout.addView(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCard(Video video){
|
public Video findVideo(String id) {
|
||||||
RelativeLayout r = new RelativeLayout(context);
|
for (int i = 0; i < resultObjects.size(); i++) {
|
||||||
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++){
|
|
||||||
Video v = resultObjects.get(i);
|
Video v = resultObjects.get(i);
|
||||||
if((v.getVideoId()).equals(id)){
|
if ((v.getVideoId()).equals(id)) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -584,20 +433,25 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void startDownload(Queue<Video> videos) {
|
private void startDownload(Queue<Video> videos) {
|
||||||
Video video;
|
Video video;
|
||||||
try{
|
if(videos.size() == 0){
|
||||||
|
mainActivity.stopDownloadService();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
video = videos.remove();
|
video = videos.remove();
|
||||||
}catch(Exception e){
|
} catch (Exception e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStoragePermissionGranted()) {
|
if (!isStoragePermissionGranted()) {
|
||||||
Toast.makeText(context, R.string.try_again_after_permission, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.try_again_after_permission, Toast.LENGTH_LONG).show();
|
||||||
|
mainActivity.stopDownloadService();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String id = video.getVideoId();
|
String id = video.getVideoId();
|
||||||
String url = "https://www.youtube.com/watch?v=" + id;
|
String url = "https://www.youtube.com/watch?v=" + id;
|
||||||
YoutubeDLRequest request = new YoutubeDLRequest(url);
|
YoutubeDLRequest request = new YoutubeDLRequest(url);
|
||||||
|
|
@ -608,7 +462,7 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
|
|
||||||
MaterialButton clickedButton = null;
|
MaterialButton clickedButton = null;
|
||||||
|
|
||||||
if(type.equals("mp3")){
|
if (type.equals("mp3")) {
|
||||||
request.addOption("--embed-thumbnail");
|
request.addOption("--embed-thumbnail");
|
||||||
request.addOption("--sponsorblock-remove", "all");
|
request.addOption("--sponsorblock-remove", "all");
|
||||||
request.addOption("--postprocessor-args", "-write_id3v1 1 -id3v2_version 3");
|
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("-x");
|
||||||
request.addOption("--audio-format", "mp3");
|
request.addOption("--audio-format", "mp3");
|
||||||
|
|
||||||
clickedButton = linearLayout.findViewWithTag(id+"##mp3");
|
clickedButton = recyclerView.findViewWithTag(id + "##mp3");
|
||||||
}else if(type.equals("mp4")){
|
} else if (type.equals("mp4")) {
|
||||||
request.addOption("--embed-thumbnail");
|
request.addOption("--embed-thumbnail");
|
||||||
request.addOption("--sponsorblock-mark", "all");
|
request.addOption("--sponsorblock-mark", "all");
|
||||||
request.addOption("--embed-subs", "");
|
request.addOption("--embed-subs", "");
|
||||||
request.addOption("-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best");
|
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");
|
request.addOption("-o", youtubeDLDir.getAbsolutePath() + "/%(title)s.%(ext)s");
|
||||||
|
|
||||||
progressBar = fragmentView.findViewWithTag(id+"##progress");
|
progressBar = fragmentView.findViewWithTag(id + "##progress");
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
//scroll to Card
|
//scroll to Card
|
||||||
View view = fragmentView.findViewWithTag(id+"##progress");
|
View view = fragmentView.findViewWithTag(id + "##progress");
|
||||||
view.getParent().requestChildFocus(view,view);
|
scrollView.requestChildFocus(view, view);
|
||||||
|
progressBar.setProgress(0);
|
||||||
showStart(video);
|
|
||||||
downloading = true;
|
downloading = true;
|
||||||
|
|
||||||
Video theVideo = video;
|
Video theVideo = video;
|
||||||
|
|
@ -647,41 +500,34 @@ public class HomeFragment extends Fragment implements View.OnClickListener{
|
||||||
progressBar.setProgress(0);
|
progressBar.setProgress(0);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
notificationManager.cancel(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID));
|
//TODO show download finished
|
||||||
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());
|
|
||||||
|
|
||||||
if(theClickedButton != null){
|
if (theClickedButton != null) {
|
||||||
if(type.equals("mp3")){
|
if (type.equals("mp3")) {
|
||||||
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
|
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
|
||||||
}else{
|
} else {
|
||||||
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
|
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addToHistory(theVideo, new Date());
|
addToHistory(theVideo, new Date());
|
||||||
updateDownloadStatusOnResult(theVideo, type);
|
updateDownloadStatusOnResult(theVideo, type);
|
||||||
downloading = false;
|
downloading = false;
|
||||||
|
|
||||||
|
|
||||||
// MEDIA SCAN
|
// MEDIA SCAN
|
||||||
MediaScannerConnection.scanFile(context, new String[]{youtubeDLDir.getAbsolutePath()}, null, null);
|
MediaScannerConnection.scanFile(context, new String[]{youtubeDLDir.getAbsolutePath()}, null, null);
|
||||||
|
|
||||||
// SCAN NEXT IN QUEUE
|
// SCAN NEXT IN QUEUE
|
||||||
startDownload(videos);
|
startDownload(videos);
|
||||||
}, e -> {
|
}, 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();
|
Toast.makeText(context, R.string.failed_download, Toast.LENGTH_LONG).show();
|
||||||
progressBar.setProgress(0);
|
progressBar.setProgress(0);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
download_notification.setContentText(getString(R.string.failed_download))
|
//TODO notification failed
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
||||||
.setProgress(0,0,false);
|
|
||||||
notificationManager.notify(Integer.parseInt(App.DOWNLOAD_CHANNEL_ID), download_notification.build());
|
|
||||||
|
|
||||||
downloading = false;
|
downloading = false;
|
||||||
|
|
||||||
// SCAN NEXT IN QUEUE
|
// 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();
|
Calendar cal = Calendar.getInstance();
|
||||||
cal.setTime(date);
|
cal.setTime(date);
|
||||||
int day = cal.get(Calendar.DAY_OF_MONTH);
|
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 time = formatter.format(date);
|
||||||
String downloadedTime = day + " " + month + " " + year + " " + time;
|
String downloadedTime = day + " " + month + " " + year + " " + time;
|
||||||
|
|
||||||
if(video != null){
|
if (video != null) {
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
try{
|
try {
|
||||||
video.setDownloadedTime(downloadedTime);
|
video.setDownloadedTime(downloadedTime);
|
||||||
dbManager.addToHistory(video);
|
dbManager.addToHistory(video);
|
||||||
}catch(Exception ignored){}
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDownloadStatusOnResult(Video v, String type){
|
public void updateDownloadStatusOnResult(Video v, String type) {
|
||||||
if(v != null){
|
if (v != null) {
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
try{
|
try {
|
||||||
dbManager.updateDownloadStatusOnResult(v.getVideoId(), type);
|
dbManager.updateDownloadStatusOnResult(v.getVideoId(), type);
|
||||||
}catch(Exception ignored){}
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
compositeDisposable.dispose();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private File getDownloadLocation(String type) {
|
private File getDownloadLocation(String type) {
|
||||||
SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
|
SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
|
||||||
String downloadsDir;
|
String downloadsDir;
|
||||||
if(type.equals("mp3")){
|
if (type.equals("mp3")) {
|
||||||
downloadsDir = sharedPreferences.getString("music_path", "");
|
downloadsDir = sharedPreferences.getString("music_path", "");
|
||||||
}else{
|
} else {
|
||||||
downloadsDir = sharedPreferences.getString("video_path", "");
|
downloadsDir = sharedPreferences.getString("video_path", "");
|
||||||
}
|
}
|
||||||
File youtubeDLDir = new File(downloadsDir);
|
File youtubeDLDir = new File(downloadsDir);
|
||||||
if (!youtubeDLDir.exists()){
|
if (!youtubeDLDir.exists()) {
|
||||||
boolean isDirCreated = youtubeDLDir.mkdir();
|
boolean isDirCreated = youtubeDLDir.mkdir();
|
||||||
if(!isDirCreated){
|
if (!isDirCreated) {
|
||||||
Toast.makeText(context, R.string.failed_making_directory, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.failed_making_directory, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return youtubeDLDir;
|
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() {
|
public boolean isStoragePermissionGranted() {
|
||||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
== PackageManager.PERMISSION_GRANTED) {
|
== 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;
|
package com.deniscerri.ytdl;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity{
|
public class MainActivity extends AppCompatActivity{
|
||||||
|
|
||||||
ActivityMainBinding binding;
|
ActivityMainBinding binding;
|
||||||
|
Context context;
|
||||||
|
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
private HomeFragment homeFragment;
|
private HomeFragment homeFragment;
|
||||||
private HistoryFragment historyFragment;
|
private HistoryFragment historyFragment;
|
||||||
|
|
@ -24,6 +28,24 @@ public class MainActivity extends AppCompatActivity{
|
||||||
private Fragment lastFragment;
|
private Fragment lastFragment;
|
||||||
private FragmentManager fm;
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
|
|
@ -32,6 +54,8 @@ public class MainActivity extends AppCompatActivity{
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
context = getBaseContext();
|
||||||
|
|
||||||
fm = getSupportFragmentManager();
|
fm = getSupportFragmentManager();
|
||||||
|
|
||||||
homeFragment = new HomeFragment();
|
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){
|
private void replaceFragment(Fragment f){
|
||||||
fm.beginTransaction().hide(lastFragment).show(f).commit();
|
fm.beginTransaction().hide(lastFragment).show(f).commit();
|
||||||
lastFragment = f;
|
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{
|
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
|
//short data
|
||||||
JSONObject res = genericRequest(url);
|
JSONObject res = genericRequest(url);
|
||||||
//extra data from the same videos
|
//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 dataArray = res.getJSONArray("items");
|
||||||
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
||||||
|
|
@ -312,9 +312,7 @@ public class YoutubeAPIManager {
|
||||||
if(seconds.isEmpty()) seconds = "00";
|
if(seconds.isEmpty()) seconds = "00";
|
||||||
else if(Integer.parseInt(seconds) < 10) seconds = "0" + seconds;
|
else if(Integer.parseInt(seconds) < 10) seconds = "0" + seconds;
|
||||||
if(minutes.isEmpty()){
|
if(minutes.isEmpty()){
|
||||||
if(hours.isEmpty()){
|
minutes = "0";
|
||||||
minutes = "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
duration = minutes + ":" + seconds;
|
duration = minutes + ":" + seconds;
|
||||||
|
|
||||||
|
|
@ -322,7 +320,6 @@ public class YoutubeAPIManager {
|
||||||
if(Integer.parseInt(minutes) < 10) minutes = "0" + minutes;
|
if(Integer.parseInt(minutes) < 10) minutes = "0" + minutes;
|
||||||
duration = hours + ":" + minutes + ":" + seconds;
|
duration = hours + ":" + minutes + ":" + seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import java.util.ArrayList;
|
||||||
public class DBManager extends SQLiteOpenHelper {
|
public class DBManager extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public static final String db_name = "ytdlnis_db";
|
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 results_table_name = "results";
|
||||||
public static final String history_table_name = "history";
|
public static final String history_table_name = "history";
|
||||||
public static final String id = "id";
|
public static final String id = "id";
|
||||||
|
|
@ -68,6 +68,24 @@ public class DBManager extends SQLiteOpenHelper {
|
||||||
" WHERE downloadedAudio=1 OR downloadedVideo=1");
|
" 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(){
|
public void clearResults(){
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
db.execSQL("DELETE FROM " + results_table_name);
|
db.execSQL("DELETE FROM " + results_table_name);
|
||||||
|
|
@ -104,15 +122,18 @@ public class DBManager extends SQLiteOpenHelper {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Video> getHistory(){
|
public ArrayList<Video> getHistory(String query){
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
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<>();
|
ArrayList<Video> list = new ArrayList<>();
|
||||||
|
|
||||||
if(cursor.moveToFirst()){
|
if(cursor.moveToFirst()){
|
||||||
do {
|
do {
|
||||||
// on below line we are adding the data from cursor to our array list.
|
// 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(2),
|
||||||
cursor.getString(3),
|
cursor.getString(3),
|
||||||
cursor.getString(4),
|
cursor.getString(4),
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,9 @@ public class Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
//HISTORY OBJECT
|
//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) {
|
String downloadedType, String downloadedTime, int isPlaylistItem) {
|
||||||
|
this.id = id;
|
||||||
this.videoId = videoId;
|
this.videoId = videoId;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.author = author;
|
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_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
||||||
app:menu="@menu/bottom_nav_menu"/>
|
app:menu="@menu/bottom_nav_menu"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -6,116 +6,105 @@
|
||||||
android:id="@+id/historycoordinator"
|
android:id="@+id/historycoordinator"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:scrollbars="vertical"
|
||||||
android:id="@+id/history_big_linearlayout"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
android:layout_marginTop="?attr/actionBarSize">
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
android:id="@+id/home_main_linearlayout"
|
||||||
android:id="@+id/swiperefresh"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
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"
|
<LinearLayout
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
android:id="@+id/historylinearLayout1"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:backgroundTintMode="src_over"
|
android:orientation="vertical" />
|
||||||
tools:context=".HomeFragment"
|
|
||||||
android:id="@+id/scrollView1"
|
<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
|
<LinearLayout
|
||||||
android:id="@+id/history_main_linearlayout"
|
android:id="@+id/shimmer_history_linear_layout"
|
||||||
android:orientation="vertical"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_margin="10dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<include layout="@layout/history_card_shimmer" />
|
||||||
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"
|
<include layout="@layout/history_card_shimmer" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<include layout="@layout/history_card_shimmer" />
|
||||||
|
|
||||||
<com.facebook.shimmer.ShimmerFrameLayout
|
<include layout="@layout/history_card_shimmer" />
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
<include layout="@layout/history_card_shimmer" />
|
||||||
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" />
|
||||||
|
|
||||||
|
|
||||||
<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>
|
</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
|
<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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/history_toolbar"
|
android:id="@+id/history_toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways|snap"
|
||||||
app:title="@string/history"
|
app:title="@string/history"
|
||||||
app:menu="@menu/history_menu"
|
app:menu="@menu/history_menu"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<include layout="@layout/history_bottom_sheet"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
@ -6,104 +6,75 @@
|
||||||
android:id="@+id/homecoordinator"
|
android:id="@+id/homecoordinator"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/home_big_linearlayout"
|
android:scrollbars="vertical"
|
||||||
android:orientation="vertical"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
android:layout_marginTop="?attr/actionBarSize"
|
|
||||||
>
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<LinearLayout
|
||||||
android:id="@+id/swiperefreshhome"
|
android:id="@+id/home_main_linearlayout"
|
||||||
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" >
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
android:visibility="visible"
|
android:id="@+id/linearLayout1"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTintMode="src_over"
|
android:orientation="vertical" />
|
||||||
tools:context=".HomeFragment"
|
|
||||||
android:id="@+id/scrollView1"
|
<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
|
<LinearLayout
|
||||||
android:id="@+id/home_main_linearlayout"
|
android:id="@+id/shimmer_linear_layout"
|
||||||
android:orientation="vertical"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_margin="10dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/result_card_shimmer" />
|
||||||
|
|
||||||
<LinearLayout
|
<include layout="@layout/result_card_shimmer" />
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/linearLayout1"
|
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<include layout="@layout/download_all_card"
|
<include layout="@layout/result_card_shimmer" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<include layout="@layout/result_card"
|
<include layout="@layout/result_card_shimmer" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</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>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</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"/>
|
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/home_appbarlayout"
|
android:id="@+id/home_appbarlayout"
|
||||||
|
app:liftOnScroll="false"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
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
|
<item
|
||||||
android:id="@+id/delete_history"
|
android:id="@+id/delete_history"
|
||||||
android:orderInCategory="100"
|
android:icon="@drawable/ic_delete_all"
|
||||||
android:title="@string/fshi_historin"
|
android:title="@string/fshi_historin"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,14 @@
|
||||||
app:showAsAction="collapseActionView|always"
|
app:showAsAction="collapseActionView|always"
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
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
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:orderInCategory="100"
|
|
||||||
android:title="@string/fshi_rezultatet"
|
android:title="@string/fshi_rezultatet"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,14 @@
|
||||||
<string name="added_to_queue">U vendos në rradhë!</string>
|
<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_already_updating">Duke u përditësuar. Prisni!</string>
|
||||||
<string name="ytdl_updating_started">Filloi Perditesimi</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>
|
</resources>
|
||||||
|
|
@ -32,4 +32,14 @@
|
||||||
<string name="ytdl_update_failed">Failed Updating</string>
|
<string name="ytdl_update_failed">Failed Updating</string>
|
||||||
<string name="ytdl_already_updating">Updating in Progress! Please Wait!</string>
|
<string name="ytdl_already_updating">Updating in Progress! Please Wait!</string>
|
||||||
<string name="ytdl_updating_started">Updating Started</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>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.yausername.ytdl;
|
package com.deniscerri.ytdl;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ ext {
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = 'com.github.yausername'
|
group = 'com.github.deniscerri'
|
||||||
version = versionName
|
version = versionName
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue