other new features

- check for updates when app launches
- remove scrollview so that recyclerview can recycle. It wont hide the top bar but thats a sacrifice for the greater good.
- remove download all mini card and made it into a floating action button
- made video loading from playlists as fast as possible even if you have thousands of videos in a list. It adds videos in recyclerview in a form of pagination
- fixed preferences when you just installed the app and you never opened the preference screen you can still download without erroring out
This commit is contained in:
Denis Çerri 2022-09-18 00:27:36 +02:00
parent b81add6008
commit 709a3778a2
No known key found for this signature in database
GPG key ID: 3F50F14A8E7F7A13
23 changed files with 505 additions and 488 deletions

View file

@ -67,13 +67,13 @@
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/download_all_card.xml" value="0.25" />
<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.2" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_home.xml" value="0.25" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_home.xml" value="0.16" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_more.xml" value="0.33" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/fragment_music_editor.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_shimmer.xml" value="0.22" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/history_card.xml" value="0.33" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/history_card_shimmer.xml" value="0.33" />
<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/home_download_all_bottom_sheet.xml" value="0.33" />
<entry key="..\:/Users/denis/Documents/GitHub/ytdlnis/app/src/main/res/layout/home_video_bottom_sheet.xml" value="0.1703125" />

View file

@ -21,6 +21,6 @@ public class ExampleInstrumentedTest {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.yausername.youtubedl_android_example", appContext.getPackageName());
assertEquals("com.deniscerri.ytdl", appContext.getPackageName());
}
}

View file

@ -1,15 +1,24 @@
package com.deniscerri.ytdl;
import android.app.Application;
import android.os.Environment;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.Toast;
import androidx.preference.PreferenceManager;
import com.deniscerri.ytdl.util.NotificationUtil;
import com.deniscerri.ytdl.util.UpdateUtil;
import com.google.android.material.color.DynamicColors;
import com.yausername.ffmpeg.FFmpeg;
import com.yausername.youtubedl_android.YoutubeDL;
import com.yausername.youtubedl_android.YoutubeDLException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.exceptions.UndeliverableException;
@ -30,6 +39,7 @@ public class App extends Application {
notificationUtil = new NotificationUtil(this);
createNotificationChannels();
PreferenceManager.setDefaultValues(this, "root_preferences", this.MODE_PRIVATE, R.xml.root_preferences, false);
configureRxJavaErrorHandler();
Completable.fromAction(this::initLibraries).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
@Override

View file

@ -10,12 +10,15 @@ import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
import com.deniscerri.ytdl.databinding.ActivityMainBinding;
import com.deniscerri.ytdl.page.HistoryFragment;
import com.deniscerri.ytdl.page.HomeFragment;
import com.deniscerri.ytdl.page.MoreFragment;
import com.deniscerri.ytdl.page.MusicEditorFragment;
import com.deniscerri.ytdl.page.settings.SettingsActivity;
import com.deniscerri.ytdl.page.settings.SettingsFragment;
import com.deniscerri.ytdl.util.UpdateUtil;
public class MainActivity extends AppCompatActivity{
@ -27,7 +30,6 @@ public class MainActivity extends AppCompatActivity{
private HomeFragment homeFragment;
private HistoryFragment historyFragment;
private MusicEditorFragment musicEditorFragment;
private MoreFragment moreFragment;
private Fragment lastFragment;
@ -58,15 +60,15 @@ public class MainActivity extends AppCompatActivity{
setContentView(R.layout.activity_main);
setContentView(binding.getRoot());
context = getBaseContext();
checkUpdate();
fm = getSupportFragmentManager();
homeFragment = new HomeFragment();
historyFragment = new HistoryFragment();
moreFragment = new MoreFragment();
musicEditorFragment = new MusicEditorFragment();
initFragments();
@ -94,8 +96,6 @@ public class MainActivity extends AppCompatActivity{
this.setTitle(getString(R.string.more));
}
replaceFragment(moreFragment);
}else if(id == R.id.music_editor){
replaceFragment(musicEditorFragment);
}
return true;
});
@ -114,9 +114,9 @@ public class MainActivity extends AppCompatActivity{
if(Intent.ACTION_SEND.equals(action)){
Log.e(TAG, action);
homeFragment = new HomeFragment();
historyFragment = new HistoryFragment();
moreFragment = new MoreFragment();
homeFragment.handleIntent(intent);
initFragments();
@ -128,10 +128,8 @@ public class MainActivity extends AppCompatActivity{
.replace(R.id.frame_layout, homeFragment)
.add(R.id.frame_layout, historyFragment)
.add(R.id.frame_layout, moreFragment)
.add(R.id.frame_layout, musicEditorFragment)
.hide(historyFragment)
.hide(moreFragment)
.hide(musicEditorFragment)
.commit();
lastFragment = homeFragment;
@ -156,4 +154,8 @@ public class MainActivity extends AppCompatActivity{
isDownloadServiceRunning = false;
}
private void checkUpdate(){
UpdateUtil updateUtil = new UpdateUtil(this);
updateUtil.updateApp();
}
}

View file

@ -4,6 +4,7 @@ import android.app.Activity;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -27,11 +28,13 @@ import java.util.ArrayList;
public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<HomeRecyclerViewAdapter.ViewHolder> {
private ArrayList<Video> videoList;
private ArrayList<MaterialCardView> checkedVideos;
private final OnItemClickListener onItemClickListener;
private Activity activity;
public HomeRecyclerViewAdapter(ArrayList<Video> videos, OnItemClickListener onItemClickListener, Activity activity){
this.videoList = videos;
this.checkedVideos = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
this.activity = activity;
}
@ -56,6 +59,7 @@ public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<HomeRecyclerVi
View cardView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.result_card, parent, false);
checkedVideos = new ArrayList<>();
return new HomeRecyclerViewAdapter.ViewHolder(cardView, onItemClickListener);
}
@ -116,15 +120,27 @@ public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<HomeRecyclerVi
card.setChecked(false);
card.setStrokeWidth(0);
card.setTag(videoID + "##card");
card.setOnClickListener(view -> {
if(card.isChecked()){
card.setStrokeWidth(0);
}else{
card.setStrokeWidth(5);
}
card.setChecked(!card.isChecked());
onItemClickListener.onCardClick(position, card.isChecked());
card.setOnLongClickListener(view -> {
checkCard(card, position);
return true;
});
card.setOnClickListener(view -> {
if(checkedVideos.size() > 0){
checkCard(card, position);
}
});
}
private void checkCard(MaterialCardView card, int position){
if(card.isChecked()){
card.setStrokeWidth(0);
checkedVideos.remove(card);
}else{
card.setStrokeWidth(5);
checkedVideos.add(card);
}
card.setChecked(!card.isChecked());
onItemClickListener.onCardClick(position, card.isChecked());
}
@Override
@ -137,9 +153,13 @@ public class HomeRecyclerViewAdapter extends RecyclerView.Adapter<HomeRecyclerVi
void onCardClick(int position, boolean add);
}
public void setVideoList(ArrayList<Video> videoList){
this.videoList = videoList;
notifyDataSetChanged();
public void setVideoList(ArrayList<Video> list, boolean reset){
int size = videoList.size();
if(reset || size == 0){
videoList = new ArrayList<>();
}
videoList.addAll(list);
notifyItemRangeInserted(size, videoList.size());
}
public void clear(){

View file

@ -14,6 +14,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
@ -46,6 +47,7 @@ public class YoutubeAPIManager {
}
public ArrayList<Video> search(String query) throws JSONException{
videos = new ArrayList<>();
//short data
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25&regionCode="+countryCODE+"&key="+key);
JSONArray dataArray = res.getJSONArray("items");
@ -91,7 +93,9 @@ public class YoutubeAPIManager {
return videos;
}
public ArrayList<Video> getPlaylist(String id, String nextPageToken) throws JSONException{
public PlaylistTuple getPlaylist(String id, String nextPageToken) throws JSONException{
videos = new ArrayList<>();
String url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken="+nextPageToken+"&maxResults=50&regionCode="+countryCODE+"&playlistId="+id+"&key="+key;
//short data
JSONObject res = genericRequest(url);
@ -131,11 +135,7 @@ public class YoutubeAPIManager {
videos.add(v);
}
String next = res.optString("nextPageToken");
if(next != ""){
ArrayList<Video> nextPage = getPlaylist(id,next);
}
return videos;
return new PlaylistTuple(next, videos);
}
@ -323,5 +323,62 @@ public class YoutubeAPIManager {
return duration;
}
// public ArrayList<String> getSearchHints(String query){
// String url = "https://google.com/complete/search?client=youtube&q="+query;
// ArrayList<String> searchHints = new ArrayList<>();
//
// BufferedReader reader;
// String line;
// StringBuilder responseContent = new StringBuilder();
// HttpURLConnection conn;
// JSONArray json;
//
// try{
// URL req = new URL(url);
// conn = (HttpURLConnection) req.openConnection();
//
// conn.setRequestMethod("GET");
// conn.setConnectTimeout(10000);
// conn.setReadTimeout(5000);
//
// if(conn.getResponseCode() < 300){
// reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// while((line = reader.readLine()) != null){
// responseContent.append(line);
// }
// reader.close();
// String content = responseContent.substring(19, responseContent.length()-1);
//
// json = new JSONArray(content);
// JSONArray hints = (JSONArray) json.get(1);
// for (int i = 0; i < hints.length(); i++){
// searchHints.add(hints.getJSONArray(i).get(0).toString());
// }
// }
// conn.disconnect();
// }catch(Exception e){
// Log.e(TAG, e.toString());
// }
// return searchHints;
// }
public class PlaylistTuple {
String nextPageToken;
ArrayList<Video> videos;
PlaylistTuple(String token, ArrayList<Video> videos){
nextPageToken = token;
this.videos = videos;
}
public String getNextPageToken() {
return nextPageToken;
}
public ArrayList<Video> getVideos() {
return videos;
}
}
}

View file

@ -153,8 +153,6 @@ public class DBManager extends SQLiteOpenHelper {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
db.execSQL("DELETE FROM " + results_table_name);
for(Video v : videot){
values.put(videoId, v.getVideoId());
values.put(title, v.getTitle());

View file

@ -23,6 +23,7 @@ import androidx.core.app.ActivityCompat;
import com.deniscerri.ytdl.BuildConfig;
import com.deniscerri.ytdl.DownloaderService;
import com.deniscerri.ytdl.R;
import com.deniscerri.ytdl.page.settings.SettingsFragment;
import com.deniscerri.ytdl.util.NotificationUtil;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
@ -113,12 +114,6 @@ public class CustomCommandActivity extends AppCompatActivity {
startDownloadService(getString(R.string.running_ytdlp_command), NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID);
text = text.substring(6).trim();
if (!isStoragePermissionGranted()) {
Toast.makeText(context, R.string.try_again_after_permission, Toast.LENGTH_LONG).show();
stopDownloadService();
return;
}
YoutubeDLRequest request = new YoutubeDLRequest(Collections.emptyList());
String commandRegex = "\"([^\"]*)\"|(\\S+)";
Matcher m = Pattern.compile(commandRegex).matcher(text);
@ -131,7 +126,7 @@ public class CustomCommandActivity extends AppCompatActivity {
}
SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
String downloadsDir = sharedPreferences.getString("command_path", "");
String downloadsDir = sharedPreferences.getString("command_path", getString(R.string.command_path));
File youtubeDLDir = new File(downloadsDir);
if (!youtubeDLDir.exists()) {
boolean isDirCreated = youtubeDLDir.mkdir();
@ -165,16 +160,6 @@ public class CustomCommandActivity extends AppCompatActivity {
compositeDisposable.add(disposable);
}
public boolean isStoragePermissionGranted() {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
public void startDownloadService(String title, int id){
if(isDownloadServiceRunning) return;
Intent serviceIntent = new Intent(context, DownloaderService.class);

View file

@ -44,7 +44,6 @@ import java.util.ArrayList;
*/
public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdapter.OnItemClickListener, View.OnClickListener{
private LinearLayout linearLayout;
private View fragmentView;
private DBManager dbManager;
Context context;
@ -52,10 +51,10 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
private ShimmerFrameLayout shimmerCards;
private MaterialToolbar topAppBar;
private RecyclerView recyclerView;
private NestedScrollView scrollView;
private HistoryRecyclerViewAdapter historyRecyclerViewAdapter;
private BottomSheetDialog bottomSheet;
private Handler uiHandler;
private RelativeLayout no_results;
private ArrayList<Video> historyObjects;
private static final String TAG = "HistoryFragment";
@ -85,16 +84,15 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
fragmentView = inflater.inflate(R.layout.fragment_history, container, false);
context = fragmentView.getContext();
layoutinflater = LayoutInflater.from(context);
linearLayout = fragmentView.findViewById(R.id.historylinearLayout1);
shimmerCards = fragmentView.findViewById(R.id.shimmer_history_framelayout);
topAppBar = fragmentView.findViewById(R.id.history_toolbar);
no_results = fragmentView.findViewById(R.id.history_no_results);
uiHandler = new Handler(Looper.getMainLooper());
dbManager = new DBManager(context);
historyObjects = new ArrayList<>();
recyclerView = fragmentView.findViewById(R.id.recycler_view_history);
scrollView = fragmentView.findViewById(R.id.history_scrollview);
historyRecyclerViewAdapter = new HistoryRecyclerViewAdapter(historyObjects, this, context);
recyclerView.setAdapter(historyRecyclerViewAdapter);
@ -110,7 +108,7 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
shimmerCards.startShimmer();
shimmerCards.setVisibility(View.VISIBLE);
historyRecyclerViewAdapter.clear();
linearLayout.removeAllViews();
no_results.setVisibility(View.GONE);
try{
Thread thread = new Thread(() -> {
historyObjects = dbManager.getHistory("");
@ -121,7 +119,7 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
});
if(historyObjects.size() == 0){
uiHandler.post(this::addNoResultsView);
uiHandler.post(() -> no_results.setVisibility(View.VISIBLE));
}
});
thread.start();
@ -132,16 +130,10 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
}
public void scrollToTop(){
scrollView.smoothScrollTo(-100,-100);
recyclerView.scrollToPosition(0);
new Handler(Looper.getMainLooper()).post(() -> ((AppBarLayout) topAppBar.getParent()).setExpanded(true, true));
}
private void addNoResultsView(){
RelativeLayout no_results = new RelativeLayout(context);
layoutinflater.inflate(R.layout.history_no_results, no_results);
linearLayout.addView(no_results);
}
private void initMenu(){
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
@Override
@ -168,7 +160,7 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
historyRecyclerViewAdapter.clear();
historyRecyclerViewAdapter.setVideoList(historyObjects);
if(historyObjects.size() == 0) addNoResultsView();
if(historyObjects.size() == 0) no_results.setVisibility(View.VISIBLE);
return true;
}
@ -180,9 +172,10 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
historyRecyclerViewAdapter.setVideoList(historyObjects);
if (historyObjects.size() == 0) {
linearLayout.removeAllViews();
addNoResultsView();
}else linearLayout.removeAllViews();
no_results.setVisibility(View.VISIBLE);
}else{
no_results.setVisibility(View.GONE);
}
return true;
}
@ -207,7 +200,7 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
delete_dialog.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> {
dbManager.clearHistory();
historyRecyclerViewAdapter.clear();
addNoResultsView();
no_results.setVisibility(View.VISIBLE);
});
delete_dialog.show();
return true;
@ -254,7 +247,7 @@ public class HistoryFragment extends Fragment implements HistoryRecyclerViewAdap
dbManager.clearHistoryItem(v);
if(historyObjects.size() == 0){
uiHandler.post(this::addNoResultsView);
uiHandler.post(() -> no_results.setVisibility(View.VISIBLE));
}
});
delete_dialog.show();

View file

@ -12,25 +12,19 @@ import android.os.Handler;
import android.os.Looper;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SearchView;
import androidx.cardview.widget.CardView;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.widget.NestedScrollView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
@ -49,7 +43,7 @@ import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.TextInputLayout;
import com.yausername.youtubedl_android.DownloadProgressCallback;
@ -79,14 +73,12 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
private View fragmentView;
private ProgressBar progressBar;
private String inputQuery;
private LinearLayout homeLinearLayout;
private RecyclerView recyclerView;
private HomeRecyclerViewAdapter homeRecyclerViewAdapter;
private NestedScrollView scrollView;
private ShimmerFrameLayout shimmerCards;
private CoordinatorLayout downloadFabs;
private CoordinatorLayout downloadAllFab;
private BottomSheetDialog bottomSheet;
private LayoutInflater layoutinflater;
Context context;
Activity activity;
@ -103,7 +95,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
private Queue<Video> downloadQueue;
MainActivity mainActivity;
private static final NotificationUtil notificationUtil = App.notificationUtil;
private final NotificationUtil notificationUtil = App.notificationUtil;
private final DownloadProgressCallback callback = new DownloadProgressCallback() {
@Override
@ -142,16 +134,14 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
context = fragmentView.getContext();
activity = getActivity();
mainActivity = (MainActivity) activity;
layoutinflater = LayoutInflater.from(context);
//initViews
homeLinearLayout = fragmentView.findViewById(R.id.linearLayout1);
shimmerCards = fragmentView.findViewById(R.id.shimmer_results_framelayout);
topAppBar = fragmentView.findViewById(R.id.home_toolbar);
scrollView = fragmentView.findViewById(R.id.home_scrollview);
recyclerView = fragmentView.findViewById(R.id.recycler_view_home);
downloadFabs = fragmentView.findViewById(R.id.home_download_fabs);
downloadFabs = fragmentView.findViewById(R.id.download_selected_coordinator);
downloadAllFab = fragmentView.findViewById(R.id.download_all_coordinator);
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(resultObjects, this, activity);
recyclerView.setAdapter(homeRecyclerViewAdapter);
@ -174,6 +164,11 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
music_fab.setOnClickListener(this);
video_fab.setOnClickListener(this);
ExtendedFloatingActionButton download_all_fab = downloadAllFab.findViewById(R.id.download_all_fab);
download_all_fab.setTag("downloadAll");
download_all_fab.setOnClickListener(this);
return fragmentView;
}
@ -182,17 +177,14 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
shimmerCards.startShimmer();
shimmerCards.setVisibility(View.VISIBLE);
homeRecyclerViewAdapter.clear();
homeLinearLayout.removeAllViews();
try {
Thread thread = new Thread(() -> {
Handler uiHandler = new Handler(Looper.getMainLooper());
dbManager = new DBManager(context);
youtubeAPIManager = new YoutubeAPIManager(context);
resultObjects = dbManager.getResults();
boolean trending = false;
if (resultObjects.size() == 0) {
trending = true;
try {
resultObjects = youtubeAPIManager.getTrending(context);
} catch (Exception e) {
@ -200,20 +192,14 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
}
}
if (resultObjects != null) {
scrollToTop();
if (trending) {
uiHandler.post(this::addTrendingText);
} else {
if (resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1) {
createDownloadAllCard();
}
uiHandler.post(this::scrollToTop);
if (resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1) {
uiHandler.post(() -> downloadAllFab.setVisibility(View.VISIBLE));
}
}
uiHandler.post(() -> {
homeRecyclerViewAdapter.setVideoList(resultObjects);
homeRecyclerViewAdapter.setVideoList(resultObjects, true);
shimmerCards.stopShimmer();
shimmerCards.setVisibility(View.GONE);
});
@ -225,16 +211,18 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
}
private void initMenu() {
youtubeAPIManager = new YoutubeAPIManager(context);
MenuItem.OnActionExpandListener onActionExpandListener = new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem menuItem) {
scrollView.setVisibility(View.GONE);
recyclerView.setVisibility(View.GONE);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
scrollView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.VISIBLE);
return true;
}
};
@ -254,39 +242,39 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
public boolean onQueryTextChange(String newText) { return false; }
});
topAppBar.setOnClickListener(view -> scrollToTop());
topAppBar.setOnMenuItemClickListener((MenuItem m) -> {
switch (m.getItemId()){
case R.id.delete_results:
dbManager.clearResults();
recyclerView.removeAllViews();
initCards();
return true;
case R.id.refresh_results:
recyclerView.removeAllViews();
initCards();
return true;
case R.id.open_settings:
Intent intent = new Intent(context, SettingsActivity.class);
startActivity(intent);
int itemId = m.getItemId();
if(itemId == R.id.delete_results){
dbManager.clearResults();
recyclerView.removeAllViews();
downloadAllFab.setVisibility(View.GONE);
initCards();
}else if(itemId == R.id.refresh_results){
recyclerView.removeAllViews();
selectedObjects = new ArrayList<>();
downloadFabs.setVisibility(View.GONE);
initCards();
}else if(itemId == R.id.open_settings){
Intent intent = new Intent(context, SettingsActivity.class);
startActivity(intent);
}
return true;
});
}
public void handleIntent(Intent intent) {
inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT);
}
public void scrollToTop() {
scrollView.smoothScrollTo(-100,-100);
recyclerView.scrollToPosition(0);
new Handler(Looper.getMainLooper()).post(() -> ((AppBarLayout) topAppBar.getParent()).setExpanded(true, true));
}
@ -294,7 +282,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
shimmerCards.startShimmer();
shimmerCards.setVisibility(View.VISIBLE);
homeRecyclerViewAdapter.clear();
homeLinearLayout.removeAllViews();
Log.e(TAG, String.valueOf(homeRecyclerViewAdapter.getItemCount()));
dbManager = new DBManager(context);
youtubeAPIManager = new YoutubeAPIManager(context);
@ -330,10 +318,10 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
} catch (Exception e) {
Log.e(TAG, e.toString());
}
dbManager.clearResults();
dbManager.addToResults(resultObjects);
new Handler(Looper.getMainLooper()).post(() -> {
homeRecyclerViewAdapter.setVideoList(resultObjects);
homeRecyclerViewAdapter.setVideoList(resultObjects, true);
//addEndofResultsText();
shimmerCards.stopShimmer();
shimmerCards.setVisibility(View.GONE);
@ -371,11 +359,11 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
Log.e(TAG, e.toString());
}
dbManager.clearResults();
dbManager.addToResults(resultObjects);
new Handler(Looper.getMainLooper()).post(() -> {
homeRecyclerViewAdapter.setVideoList(resultObjects);
homeRecyclerViewAdapter.setVideoList(resultObjects, true);
shimmerCards.stopShimmer();
shimmerCards.setVisibility(View.GONE);
});
@ -396,22 +384,35 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
@Override
public void run() {
try {
resultObjects = youtubeAPIManager.getPlaylist(query, "");
String nextPageToken = "";
dbManager.clearResults();
homeRecyclerViewAdapter.setVideoList(new ArrayList<>(), true);
do {
YoutubeAPIManager.PlaylistTuple tmp = youtubeAPIManager.getPlaylist(query, nextPageToken);
ArrayList<Video> tmp_vids = tmp.getVideos();
String tmp_token = tmp.getNextPageToken();
resultObjects.addAll(tmp_vids);
new Handler(Looper.getMainLooper()).post(() -> {
homeRecyclerViewAdapter.setVideoList(tmp_vids, false);
//addEndofResultsText();
shimmerCards.stopShimmer();
shimmerCards.setVisibility(View.GONE);
});
Thread.sleep(1000);
dbManager.addToResults(tmp_vids);
if (tmp_token.isEmpty()) break;
if (tmp_token.equals(nextPageToken)) break;
nextPageToken = tmp_token;
}while (true);
// DOWNLOAD ALL BUTTON
if (resultObjects.size() > 1) {
new Handler(Looper.getMainLooper()).post(() -> downloadAllFab.setVisibility(View.VISIBLE));
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
dbManager.addToResults(resultObjects);
// DOWNLOAD ALL BUTTON
if (resultObjects.size() > 1) {
createDownloadAllCard();
}
new Handler(Looper.getMainLooper()).post(() -> {
homeRecyclerViewAdapter.setVideoList(resultObjects);
//addEndofResultsText();
shimmerCards.stopShimmer();
shimmerCards.setVisibility(View.GONE);
});
}
});
thread.start();
@ -424,24 +425,6 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
}
private void addTrendingText(){
TextView trendingText = new TextView(context);
trendingText.setText(R.string.Trending);
trendingText.setPadding(70, 0 , 0, 0);
homeLinearLayout.addView(trendingText);
}
private void createDownloadAllCard() {
RelativeLayout r = new RelativeLayout(context);
layoutinflater.inflate(R.layout.download_all_card, r);
CardView card = r.findViewById(R.id.downloadall_card_view);
card.setTag("downloadAll");
card.setOnClickListener(this);
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(() -> homeLinearLayout.addView(r));
}
public Video findVideo(String id) {
for (int i = 0; i < resultObjects.size(); i++) {
@ -465,11 +448,6 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
try {
video = videos.peek();
} catch (Exception e) {
return;
}
if (!isStoragePermissionGranted()) {
Toast.makeText(context, R.string.try_again_after_permission, Toast.LENGTH_LONG).show();
mainActivity.stopDownloadService();
return;
}
@ -548,8 +526,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
progressBar.setVisibility(View.VISIBLE);
//scroll to Card
View view = fragmentView.findViewWithTag(id + "##progress");
scrollView.requestChildFocus(view, view);
recyclerView.scrollToPosition(resultObjects.indexOf(findVideo(id)));
progressBar.setProgress(0);
downloading = true;
@ -562,8 +539,6 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
progressBar.setProgress(0);
progressBar.setVisibility(View.GONE);
//TODO show download finished
if (theClickedButton != null) {
if (type.equals("mp3")) {
theClickedButton.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
@ -589,8 +564,6 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
Toast.makeText(context, R.string.failed_download, Toast.LENGTH_LONG).show();
progressBar.setProgress(0);
progressBar.setVisibility(View.GONE);
//TODO notification failed
downloading = false;
// SCAN NEXT IN QUEUE
@ -639,10 +612,11 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
String downloadsDir;
if (type.equals("mp3")) {
downloadsDir = sharedPreferences.getString("music_path", "");
downloadsDir = sharedPreferences.getString("music_path", getString(R.string.music_path));
} else {
downloadsDir = sharedPreferences.getString("video_path", "");
downloadsDir = sharedPreferences.getString("video_path", getString(R.string.video_path));
}
File youtubeDLDir = new File(downloadsDir);
if (!youtubeDLDir.exists()) {
boolean isDirCreated = youtubeDLDir.mkdir();
@ -653,16 +627,6 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
return youtubeDLDir;
}
public boolean isStoragePermissionGranted() {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
@Override
public void onButtonClick(int position, String type) {
Log.e(TAG, type);
@ -675,15 +639,38 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
return;
}
mainActivity.startDownloadService(vid.getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
if (isStoragePermissionGranted()){
mainActivity.startDownloadService(vid.getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
}
}
public boolean isStoragePermissionGranted() {
if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
}else{
downloadQueue = new LinkedList<>();
ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
@Override
public void onCardClick(int position, boolean add) {
Video video = resultObjects.get(position);
if (add) selectedObjects.add(video); else selectedObjects.remove(video);
if(selectedObjects.size() > 1) downloadFabs.setVisibility(View.VISIBLE); else downloadFabs.setVisibility(View.GONE);
if(selectedObjects.size() > 1){
downloadAllFab.setVisibility(View.GONE);
downloadFabs.setVisibility(View.VISIBLE);
} else {
downloadFabs.setVisibility(View.GONE);
if (resultObjects.size() > 1 && resultObjects.get(1).getIsPlaylistItem() == 1) {
downloadAllFab.setVisibility(View.VISIBLE);
}
}
}
@ -715,8 +702,10 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
return;
}
mainActivity.startDownloadService(downloadQueue.peek().getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
if(isStoragePermissionGranted()){
mainActivity.startDownloadService(downloadQueue.peek().getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
}
}
}
if (viewIdName.equals("downloadAll")){
@ -782,7 +771,9 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
Toast.makeText(context, R.string.added_to_queue, Toast.LENGTH_LONG).show();
return;
}
mainActivity.startDownloadService(downloadQueue.peek().getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
if(isStoragePermissionGranted()){
mainActivity.startDownloadService(downloadQueue.peek().getTitle(), NotificationUtil.DOWNLOAD_NOTIFICATION_ID);
startDownload(downloadQueue);
}
}
}

View file

@ -29,12 +29,8 @@ import io.reactivex.schedulers.Schedulers;
public class MoreFragment extends PreferenceFragmentCompat {
Preference updateYTDL;
Preference updateApp;
MaterialCardView materialCardView;
public static boolean updatingYTDL;
public static boolean updatingApp;
public static final String TAG = "MoreFragment";
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
private UpdateUtil updateUtil;
@Override
@ -44,94 +40,16 @@ public class MoreFragment extends PreferenceFragmentCompat {
updateYTDL = findPreference("update_ytdl");
updateApp = findPreference("update_app");
updateUtil = new UpdateUtil(getContext());
updateYTDL.setOnPreferenceClickListener(preference -> {
updateYoutubeDL();
updateUtil.updateYoutubeDL();
return true;
});
updateApp.setOnPreferenceClickListener(preference -> {
updateApp();
updateUtil.updateApp();
return true;
});
}
private void updateYoutubeDL() {
if (updatingYTDL) {
Toast.makeText(getContext(), getString(R.string.ytdl_already_updating), Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(getContext(), getString(R.string.ytdl_updating_started), Toast.LENGTH_SHORT).show();
updatingYTDL = true;
Disposable disposable = Observable.fromCallable(() -> YoutubeDL.getInstance().updateYoutubeDL(getContext()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> {
switch (status) {
case DONE:
Toast.makeText(getContext(), getString(R.string.ytld_update_success), Toast.LENGTH_LONG).show();
break;
case ALREADY_UP_TO_DATE:
Toast.makeText(getContext(), getString(R.string.you_are_in_latest_version), Toast.LENGTH_LONG).show();
break;
default:
Toast.makeText(getContext(), status.toString(), Toast.LENGTH_LONG).show();
break;
}
updatingYTDL = false;
}, e -> {
if(BuildConfig.DEBUG) Log.e(TAG, getString(R.string.ytdl_update_failed), e);
Toast.makeText(getContext(), getString(R.string.ytdl_update_failed), Toast.LENGTH_LONG).show();
updatingYTDL = false;
});
compositeDisposable.add(disposable);
}
private void updateApp(){
if (updatingApp) {
Toast.makeText(getContext(), getString(R.string.ytdl_already_updating), Toast.LENGTH_LONG).show();
return;
}
UpdateUtil updateUtil = new UpdateUtil(getContext());
AtomicReference<JSONObject> res = new AtomicReference<>(new JSONObject());
try{
Thread thread = new Thread(() -> res.set(updateUtil.checkForAppUpdate()));
thread.start();
thread.join();
}catch(Exception e){
Log.e(TAG, e.toString());
}
if(res.get() == null){
Toast.makeText(getContext(), R.string.error_checking_latest_version, Toast.LENGTH_SHORT).show();
}
String version = "";
String body = "";
try {
version = res.get().getString("tag_name");
body = res.get().getString("body");
} catch (JSONException ignored) {}
if(version.equals("v"+BuildConfig.VERSION_NAME)){
getActivity().runOnUiThread(() -> Toast.makeText(getContext(), R.string.you_are_running_latest_version, Toast.LENGTH_SHORT).show());
return;
}
MaterialAlertDialogBuilder updateDialog = new MaterialAlertDialogBuilder(getContext())
.setTitle(version)
.setMessage(body)
.setIcon(R.drawable.ic_update_app)
.setNegativeButton("Cancel", (dialogInterface, i) -> {})
.setPositiveButton("Update", (dialogInterface, i) -> updateUtil.updateApp(res.get()));
updateDialog.show();
}
}

View file

@ -1,30 +0,0 @@
package com.deniscerri.ytdl.page;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.deniscerri.ytdl.R;
public class MusicEditorFragment extends Fragment {
public MusicEditorFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_music_editor, container, false);
}
}

View file

@ -1,8 +1,11 @@
package com.deniscerri.ytdl.page.settings;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
@ -12,6 +15,7 @@ import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@ -20,6 +24,7 @@ import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreferenceCompat;
import com.deniscerri.ytdl.BuildConfig;
import com.deniscerri.ytdl.R;
import com.deniscerri.ytdl.util.UpdateUtil;
import com.yausername.youtubedl_android.YoutubeDL;
import java.nio.charset.StandardCharsets;
@ -49,10 +54,11 @@ public class SettingsFragment extends PreferenceFragmentCompat {
Preference version;
private UpdateUtil updateUtil;
public static final int MUSIC_PATH_CODE = 33333;
public static final int VIDEO_PATH_CODE = 55555;
public static final int COMMAND_PATH_CODE = 77777;
private static boolean firstRun = true;
private static final String TAG = "SettingsFragment";
@ -60,6 +66,16 @@ public class SettingsFragment extends PreferenceFragmentCompat {
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
updateUtil = new UpdateUtil(requireContext());
initPreferences();
initListeners();
}
private void initPreferences(){
SharedPreferences preferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
musicPath = findPreference("music_path");
videoPath = findPreference("video_path");
commandPath = findPreference("command_path");
@ -79,24 +95,14 @@ public class SettingsFragment extends PreferenceFragmentCompat {
version = findPreference("version");
version.setSummary(BuildConfig.VERSION_NAME);
if(firstRun){
initPreferences();
if (preferences.getString("music_path", "").isEmpty()){
editor.putString("music_path", getString(R.string.music_path));
}
initListeners();
}
private void initPreferences(){
SharedPreferences preferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
if(preferences.getString("music_path", "").isEmpty()){
editor.putString("music_path", "/storage/emulated/0/Music/");
if (preferences.getString("video_path", "").isEmpty()){
editor.putString("video_path", getString(R.string.video_path));
}
if(preferences.getString("video_path", "").isEmpty()){
editor.putString("video_path", "/storage/emulated/0/Movies/");
}
if(preferences.getString("command_path", "").isEmpty()){
editor.putString("command_path", "/storage/emulated/0/Download");
if (preferences.getString("command_path", "").isEmpty()){
editor.putString("command_path", getString(R.string.command_path));
}
editor.putInt("concurrent_fragments", concurrentFragments.getValue());
@ -111,7 +117,6 @@ public class SettingsFragment extends PreferenceFragmentCompat {
editor.putInt("audio_quality", audioQuality.getValue());
editor.apply();
firstRun = false;
}
private void initListeners(){
@ -209,6 +214,11 @@ public class SettingsFragment extends PreferenceFragmentCompat {
return true;
});
version.setOnPreferenceClickListener(preference -> {
updateUtil.updateApp();
return true;
});
}
ActivityResultLauncher<Intent> musicPathResultLauncher = registerForActivityResult(

View file

@ -10,25 +10,81 @@ import android.util.Log;
import android.widget.Toast;
import com.deniscerri.ytdl.App;
import com.deniscerri.ytdl.BuildConfig;
import com.deniscerri.ytdl.R;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.yausername.youtubedl_android.YoutubeDL;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.atomic.AtomicReference;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class UpdateUtil {
Context context;
private final String TAG = "UpdateUtil";
private DownloadManager downloadManager;
public static boolean updatingYTDL;
public static boolean updatingApp;
MaterialCardView materialCardView;
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
public UpdateUtil(Context context){
this.context = context;
downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
}
public void updateApp(){
if (updatingApp) {
Toast.makeText(context, context.getString(R.string.ytdl_already_updating), Toast.LENGTH_LONG).show();
return;
}
AtomicReference<JSONObject> res = new AtomicReference<>(new JSONObject());
try{
Thread thread = new Thread(() -> res.set(checkForAppUpdate()));
thread.start();
thread.join();
}catch(Exception e){
Log.e(TAG, e.toString());
}
if(res.get() == null){
Toast.makeText(context, R.string.error_checking_latest_version, Toast.LENGTH_SHORT).show();
}
String version = "";
String body = "";
try {
version = res.get().getString("tag_name");
body = res.get().getString("body");
} catch (JSONException ignored) {}
if(version.equals("v"+BuildConfig.VERSION_NAME)){
Toast.makeText(context, R.string.you_are_in_latest_version, Toast.LENGTH_SHORT).show();
return;
}
MaterialAlertDialogBuilder updateDialog = new MaterialAlertDialogBuilder(context)
.setTitle(version)
.setMessage(body)
.setIcon(R.drawable.ic_update_app)
.setNegativeButton("Cancel", (dialogInterface, i) -> {})
.setPositiveButton("Update", (dialogInterface, i) -> startAppUpdate(res.get()));
updateDialog.show();
}
public JSONObject checkForAppUpdate(){
String url = "https://api.github.com/repos/deniscerri/ytdlnis/releases/latest";
@ -69,7 +125,7 @@ public class UpdateUtil {
return json;
}
public void updateApp(JSONObject updateInfo){
private void startAppUpdate(JSONObject updateInfo){
try{
JSONArray versions = updateInfo.getJSONArray("assets");
String url = "";
@ -95,7 +151,7 @@ public class UpdateUtil {
downloadManager.enqueue(new DownloadManager.Request(uri)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI |
DownloadManager.Request.NETWORK_MOBILE)
DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setTitle(context.getString(R.string.downloading_update))
@ -104,5 +160,38 @@ public class UpdateUtil {
}catch(Exception ignored){}
}
public void updateYoutubeDL() {
if (updatingYTDL) {
Toast.makeText(context, context.getString(R.string.ytdl_already_updating), Toast.LENGTH_LONG).show();
return;
}
Toast.makeText(context, context.getString(R.string.ytdl_updating_started), Toast.LENGTH_SHORT).show();
updatingYTDL = true;
Disposable disposable = Observable.fromCallable(() -> YoutubeDL.getInstance().updateYoutubeDL(context))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> {
switch (status) {
case DONE:
Toast.makeText(context, context.getString(R.string.ytld_update_success), Toast.LENGTH_LONG).show();
break;
case ALREADY_UP_TO_DATE:
Toast.makeText(context, context.getString(R.string.you_are_in_latest_version), Toast.LENGTH_LONG).show();
break;
default:
Toast.makeText(context, status.toString(), Toast.LENGTH_LONG).show();
break;
}
updatingYTDL = false;
}, e -> {
if(BuildConfig.DEBUG) Log.e(TAG, context.getString(R.string.ytdl_update_failed), e);
Toast.makeText(context, context.getString(R.string.ytdl_update_failed), Toast.LENGTH_LONG).show();
updatingYTDL = false;
});
compositeDisposable.add(disposable);
}
}

View file

@ -6,86 +6,6 @@
android:id="@+id/historycoordinator"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.core.widget.NestedScrollView
android:id="@+id/history_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout
android:id="@+id/home_main_linearlayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/historylinearLayout1"
android:layout_width="match_parent"
android:layout_gravity="center"
android:layout_height="match_parent"
android:orientation="vertical" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="@+id/recycler_view_history"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:nestedScrollingEnabled="false"
android:layout_margin="10dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
>
</androidx.recyclerview.widget.RecyclerView>
<com.facebook.shimmer.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shimmer_history_framelayout"
android:orientation="vertical">
<LinearLayout
android:id="@+id/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" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/history_appbarlayout"
app:liftOnScroll="false"
@ -105,6 +25,64 @@
</com.google.android.material.appbar.AppBarLayout>
<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:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
>
</androidx.recyclerview.widget.RecyclerView>
<include layout="@layout/history_no_results"
android:visibility="gone" />
<com.facebook.shimmer.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:id="@+id/shimmer_history_framelayout"
android:orientation="vertical">
<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" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<include layout="@layout/history_bottom_sheet"
android:visibility="gone" />

View file

@ -6,74 +6,9 @@
android:id="@+id/homecoordinator"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.core.widget.NestedScrollView
android:id="@+id/home_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout
android:id="@+id/home_main_linearlayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="@+id/recycler_view_home"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:nestedScrollingEnabled="false"
android:layout_margin="10dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
>
</androidx.recyclerview.widget.RecyclerView>
<com.facebook.shimmer.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shimmer_results_framelayout"
android:orientation="vertical">
<LinearLayout
android:id="@+id/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" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/home_appbarlayout"
app:liftOnScroll="false"
app:liftOnScroll="true"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@ -90,8 +25,72 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="@+id/recycler_view_home"
android:nestedScrollingEnabled="false"
android:orientation="vertical"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:clipToPadding="false"
android:paddingBottom="150dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
<com.facebook.shimmer.ShimmerFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shimmer_results_framelayout"
android:layout_marginTop="?attr/actionBarSize"
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" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/home_download_fabs"
android:id="@+id/download_all_coordinator"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/download_all_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_gravity="bottom|end"
android:text="@string/download_all"
app:icon="@drawable/ic_down"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/download_selected_coordinator"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="match_parent">
@ -108,7 +107,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/colorPrimaryContainer"
android:padding="5dp"
android:orientation="horizontal">
<com.google.android.material.floatingactionbutton.FloatingActionButton
@ -126,6 +124,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginLeft="5dp"
app:srcCompat="@drawable/ic_video"/>
</LinearLayout>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".page.MusicEditorFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="ello" />
</FrameLayout>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/history_shimmer_relative_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_height="150dp"
app:layout_constraintDimensionRatio="H,2.66:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout
android:id="@+id/history_no_results"
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">

View file

@ -7,9 +7,6 @@
<item android:title="@string/history"
android:icon="@drawable/ic_history"
android:id="@+id/history"/>
<item android:title="@string/music_editor"
android:icon="@drawable/ic_music_downloaded"
android:id="@+id/music_editor"/>
<item android:title="@string/more"
android:icon="@drawable/ic_more"
android:id="@+id/more"/>

View file

@ -53,8 +53,15 @@
<string name="run">Ekzekuto</string>
<string name="running_ytdlp_command">Duke Ekzekutuar Komandën yt-dlp</string>
<string name="command_directory">Direktoria e Komandës</string>
<string name="you_are_running_latest_version">Ti je në versionin e fundit</string>
<string name="error_checking_latest_version">Gabim në kontrollin e versionit!</string>
<string name="couldnt_find_apk">Nuk u gjet një apk e vlefshme!</string>
<string name="downloading_update">Duke Përditësuar</string>
<string name="chooose_range">Zgjidh Shtrirjen</string>
<string name="audio">Audio</string>
<string name="video">Video</string>
<string name="first">E para</string>
<string name="last">E fundit</string>
<string name="choose_range_desc">Cilëso cilën pjesë të listës do të shkarkosh. Lëre siç është që t\'i shkarkosh të gjitha</string>
<string name="last_cant_be_smaller_than_first">Indeksi i fundit nuk mund të jetë më i vogël se indeksi i parë</string>
<string name="first_cant_be_larger_than_last">Indeksi i parë nuk mund të jetë më i madh se indeksi i fundit</string>
</resources>

View file

@ -53,7 +53,6 @@
<string name="run">Run</string>
<string name="running_ytdlp_command">Running yt-dlp Custom Command</string>
<string name="command_directory">Custom Command Directory</string>
<string name="you_are_running_latest_version">You are running the latest version!</string>
<string name="error_checking_latest_version">Error checking for latest version!</string>
<string name="couldnt_find_apk">Couldn\'t find a suitable apk!</string>
<string name="downloading_update">Downloading Update</string>
@ -65,4 +64,7 @@
<string name="choose_range_desc">Write the range to download from the playlist. Leave as it is to download everything!</string>
<string name="last_cant_be_smaller_than_first">Last index cannot be smaller than the first!</string>
<string name="first_cant_be_larger_than_last">First index cannot be larger than the last!</string>
<string name="music_path" translatable="false">/storage/emulated/0/Download</string>
<string name="video_path" translatable="false">/storage/emulated/0/Download</string>
<string name="command_path" translatable="false">/storage/emulated/0/Download</string>
</resources>

View file

@ -5,16 +5,19 @@
<Preference
app:icon="@drawable/ic_music_downloaded"
app:key="music_path"
app:defaultValue="@string/music_path"
app:title="@string/music_directory" />
<Preference
app:icon="@drawable/ic_video_downloaded"
app:key="video_path"
app:defaultValue="@string/video_path"
app:title="@string/video_directory" />
<Preference
app:icon="@drawable/ic_baseline_keyboard_arrow_right_24"
app:key="command_path"
app:defaultValue="@string/command_path"
app:title="@string/command_directory" />
</PreferenceCategory>
@ -108,6 +111,6 @@
app:allowDividerAbove="true"
app:icon="@drawable/ic_info"
app:key="version"
app:title="@string/version"></Preference>
app:title="@string/version"/>
</PreferenceScreen>