added invidous support for youtube queries
also added search suggestions
This commit is contained in:
parent
8dff21bc8a
commit
4b5dc1fb40
10 changed files with 324 additions and 86 deletions
|
|
@ -320,7 +320,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/file_open/baseline_file_open_24.xml" />
|
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material/icons/materialicons/arrow_upward/baseline_arrow_upward_24.xml" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
|
|
@ -331,7 +331,7 @@
|
||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
<entry key="color" value="00a6ff" />
|
<entry key="color" value="00a6ff" />
|
||||||
<entry key="outputName" value="ic_baseline_file_open_24" />
|
<entry key="outputName" value="ic_arrow_up" />
|
||||||
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
|
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppDefaultTheme" >
|
android:theme="@style/AppDefaultTheme" >
|
||||||
<activity android:name=".MainActivity"
|
<activity android:name=".MainActivity"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,7 @@ public class DownloaderService extends Service {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}catch (Exception ignored){}
|
}catch (Exception ignored){}
|
||||||
|
onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDownloadEnd(){
|
private void onDownloadEnd(){
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,13 @@ import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
|
@ -134,6 +138,14 @@ public class MainActivity extends AppCompatActivity{
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getWindow().getDecorView().setOnApplyWindowInsetsListener((view, windowInsets) -> {
|
||||||
|
WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(windowInsets, view);
|
||||||
|
boolean isImeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime());
|
||||||
|
binding.bottomNavigationView.setVisibility(isImeVisible ? View.GONE : View.VISIBLE);
|
||||||
|
view.onApplyWindowInsets(windowInsets);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
|
||||||
askPermissions();
|
askPermissions();
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,12 @@ import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.AutoCompleteTextView;
|
import android.widget.AutoCompleteTextView;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
@ -44,6 +49,9 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator;
|
import com.google.android.material.progressindicator.LinearProgressIndicator;
|
||||||
import com.google.android.material.textfield.TextInputLayout;
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
|
import com.google.android.material.textview.MaterialTextView;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
@ -59,6 +67,8 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
private int inputQueriesLength = 0;
|
private int inputQueriesLength = 0;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private HomeRecyclerViewAdapter homeRecyclerViewAdapter;
|
private HomeRecyclerViewAdapter homeRecyclerViewAdapter;
|
||||||
|
private ScrollView searchSuggestions;
|
||||||
|
private LinearLayout searchSuggestionsLinearLayout;
|
||||||
private ShimmerFrameLayout shimmerCards;
|
private ShimmerFrameLayout shimmerCards;
|
||||||
private CoordinatorLayout downloadFabs;
|
private CoordinatorLayout downloadFabs;
|
||||||
private CoordinatorLayout downloadAllFab;
|
private CoordinatorLayout downloadAllFab;
|
||||||
|
|
@ -220,6 +230,8 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
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);
|
||||||
recyclerView = fragmentView.findViewById(R.id.recycler_view_home);
|
recyclerView = fragmentView.findViewById(R.id.recycler_view_home);
|
||||||
|
searchSuggestions = fragmentView.findViewById(R.id.search_suggestions_scroll_view);
|
||||||
|
searchSuggestionsLinearLayout = fragmentView.findViewById(R.id.search_suggestions_linear_layout);
|
||||||
|
|
||||||
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(resultObjects, this, activity);
|
homeRecyclerViewAdapter = new HomeRecyclerViewAdapter(resultObjects, this, activity);
|
||||||
recyclerView.setAdapter(homeRecyclerViewAdapter);
|
recyclerView.setAdapter(homeRecyclerViewAdapter);
|
||||||
|
|
@ -347,6 +359,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
public boolean onMenuItemActionExpand(MenuItem menuItem) {
|
||||||
homeFabs.setVisibility(View.GONE);
|
homeFabs.setVisibility(View.GONE);
|
||||||
recyclerView.setVisibility(View.GONE);
|
recyclerView.setVisibility(View.GONE);
|
||||||
|
searchSuggestions.setVisibility(View.VISIBLE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,6 +367,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
|
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
|
||||||
homeFabs.setVisibility(View.VISIBLE);
|
homeFabs.setVisibility(View.VISIBLE);
|
||||||
recyclerView.setVisibility(View.VISIBLE);
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
|
searchSuggestions.setVisibility(View.GONE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -368,6 +382,7 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
searchView.setQueryHint(getString(R.string.search_hint));
|
searchView.setQueryHint(getString(R.string.search_hint));
|
||||||
|
|
||||||
dbManager = new DBManager(context);
|
dbManager = new DBManager(context);
|
||||||
|
infoUtil = new InfoUtil(context);
|
||||||
|
|
||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -394,7 +409,25 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextChange(String newText) { return false; }
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
searchSuggestionsLinearLayout.removeAllViews();
|
||||||
|
if (newText.isEmpty()) return false;
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
ArrayList<String> suggestions = infoUtil.getSearchSuggestions(newText);
|
||||||
|
for (int i = 0; i < suggestions.size();i++){
|
||||||
|
View v = LayoutInflater.from(fragmentContext).inflate(R.layout.search_suggestion_item, null);
|
||||||
|
TextView textView = v.findViewById(R.id.suggestion_text);
|
||||||
|
textView.setText(suggestions.get(i));
|
||||||
|
new Handler(Looper.getMainLooper()).post(() -> searchSuggestionsLinearLayout.addView(v));
|
||||||
|
textView.setOnClickListener(view -> onQueryTextSubmit(textView.getText().toString()));
|
||||||
|
|
||||||
|
MaterialButton mb = v.findViewById(R.id.set_search_query_button);
|
||||||
|
mb.setOnClickListener(view -> searchView.setQuery(textView.getText(), false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
topAppBar.setOnClickListener(view -> scrollToTop());
|
topAppBar.setOnClickListener(view -> scrollToTop());
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,11 @@ import java.util.Locale;
|
||||||
|
|
||||||
public class InfoUtil {
|
public class InfoUtil {
|
||||||
private static final String TAG = "API MANAGER";
|
private static final String TAG = "API MANAGER";
|
||||||
private static String countryCODE = "US";
|
private static final String invidousURL = "https://invidious.baczek.me/api/v1/";
|
||||||
|
private static String countryCODE;
|
||||||
private ArrayList<Video> videos;
|
private ArrayList<Video> videos;
|
||||||
private String key;
|
private String key;
|
||||||
|
private boolean useInvidous;
|
||||||
private DBManager dbManager;
|
private DBManager dbManager;
|
||||||
|
|
||||||
public InfoUtil(Context context) {
|
public InfoUtil(Context context) {
|
||||||
|
|
@ -43,22 +45,33 @@ public class InfoUtil {
|
||||||
JSONObject country = genericRequest("https://ipwho.is/");
|
JSONObject country = genericRequest("https://ipwho.is/");
|
||||||
try{
|
try{
|
||||||
countryCODE = country.getString("country_code");
|
countryCODE = country.getString("country_code");
|
||||||
}catch(Exception ignored){}
|
}catch(Exception e){
|
||||||
|
countryCODE = "US";
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject res = genericRequest(invidousURL + "stats");
|
||||||
|
if(res.length() == 0) useInvidous = false;
|
||||||
|
else useInvidous = true;
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
thread.join();
|
thread.join();
|
||||||
|
}catch(Exception e){
|
||||||
}catch(Exception ignored){
|
e.printStackTrace();
|
||||||
Toast.makeText(context, "Couldn't find API Key for the request", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
videos = new ArrayList<>();
|
videos = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Video> search(String query) throws JSONException{
|
public ArrayList<Video> search(String query) throws JSONException{
|
||||||
if (key.isEmpty()) return getFromYTDL(query);
|
|
||||||
|
|
||||||
videos = new ArrayList<>();
|
videos = new ArrayList<>();
|
||||||
|
if (key.isEmpty()) {
|
||||||
|
if(useInvidous) return searchFromInvidous(query);
|
||||||
|
else return getFromYTDL(query);
|
||||||
|
}
|
||||||
|
return searchFromKey(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Video> searchFromKey(String query) throws JSONException{
|
||||||
//short data
|
//short data
|
||||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25®ionCode="+countryCODE+"&key="+key);
|
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25®ionCode="+countryCODE+"&key="+key);
|
||||||
if (!res.has("items")) return getFromYTDL(query);
|
if (!res.has("items")) return getFromYTDL(query);
|
||||||
|
|
@ -91,7 +104,7 @@ public class InfoUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
snippet.put("duration", duration);
|
snippet.put("duration", duration);
|
||||||
snippet = fixThumbnail(snippet);
|
fixThumbnail(snippet);
|
||||||
Video v = createVideofromJSON(snippet);
|
Video v = createVideofromJSON(snippet);
|
||||||
|
|
||||||
if(v == null || v.getThumb().isEmpty()){
|
if(v == null || v.getThumb().isEmpty()){
|
||||||
|
|
@ -105,6 +118,32 @@ public class InfoUtil {
|
||||||
return videos;
|
return videos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<Video> searchFromInvidous(String query) throws JSONException{
|
||||||
|
JSONObject res = genericRequest(invidousURL + "search?q="+query);
|
||||||
|
if(res.length() == 0) return getFromYTDL(query);
|
||||||
|
JSONArray dataArray = res.getJSONArray("");
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for(int i = 0; i < dataArray.length(); i++){
|
||||||
|
JSONObject element = dataArray.getJSONObject(i);
|
||||||
|
String duration = formatIntegerDuration(element.getInt("lengthSeconds"));
|
||||||
|
if(duration.equals("0:00")){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
element.put("duration", duration);
|
||||||
|
element.put("thumb", element.getJSONArray("videoThumbnails").getJSONObject(0).getString("url"));
|
||||||
|
Video v = createVideofromInvidiousJSON(element);
|
||||||
|
|
||||||
|
if(v == null || v.getThumb().isEmpty()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
videos.add(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return videos;
|
||||||
|
}
|
||||||
|
|
||||||
public PlaylistTuple getPlaylist(String id, String nextPageToken) throws JSONException{
|
public PlaylistTuple getPlaylist(String id, String nextPageToken) throws JSONException{
|
||||||
if (key.isEmpty()) return new PlaylistTuple("", getFromYTDL("https://www.youtube.com/playlist?list="+id));
|
if (key.isEmpty()) return new PlaylistTuple("", getFromYTDL("https://www.youtube.com/playlist?list="+id));
|
||||||
videos = new ArrayList<>();
|
videos = new ArrayList<>();
|
||||||
|
|
@ -136,7 +175,7 @@ public class InfoUtil {
|
||||||
String duration = extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails").getString("duration");
|
String duration = extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails").getString("duration");
|
||||||
duration = formatDuration(duration);
|
duration = formatDuration(duration);
|
||||||
snippet.put("duration", duration);
|
snippet.put("duration", duration);
|
||||||
snippet = fixThumbnail(snippet);
|
fixThumbnail(snippet);
|
||||||
Video v = createVideofromJSON(snippet);
|
Video v = createVideofromJSON(snippet);
|
||||||
|
|
||||||
if(v == null || v.getThumb().isEmpty()){
|
if(v == null || v.getThumb().isEmpty()){
|
||||||
|
|
@ -152,9 +191,16 @@ public class InfoUtil {
|
||||||
return new PlaylistTuple(next, videos);
|
return new PlaylistTuple(next, videos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Video getVideo(String id) throws JSONException {
|
public Video getVideo(String id) throws JSONException {
|
||||||
if (key.isEmpty()) return getFromYTDL("https://www.youtube.com/watch?v="+id).get(0);
|
if (key.isEmpty()) {
|
||||||
|
if (useInvidous){
|
||||||
|
JSONObject res = genericRequest(invidousURL + "videos/"+id);
|
||||||
|
if (res.length() == 0) return getFromYTDL("https://www.youtube.com/watch?v="+id).get(0);
|
||||||
|
return createVideofromInvidiousJSON(res);
|
||||||
|
}else{
|
||||||
|
return getFromYTDL("https://www.youtube.com/watch?v="+id).get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//short data
|
//short data
|
||||||
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id="+id+"&key="+key);
|
JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id="+id+"&key="+key);
|
||||||
|
|
@ -166,10 +212,9 @@ public class InfoUtil {
|
||||||
|
|
||||||
res.put("videoID", id);
|
res.put("videoID", id);
|
||||||
res.put("duration", duration);
|
res.put("duration", duration);
|
||||||
res = fixThumbnail(res);
|
fixThumbnail(res);
|
||||||
|
|
||||||
Video v = createVideofromJSON(res);
|
return createVideofromJSON(res);
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Video createVideofromJSON(JSONObject obj){
|
private Video createVideofromJSON(JSONObject obj){
|
||||||
|
|
@ -198,6 +243,31 @@ public class InfoUtil {
|
||||||
return video;
|
return video;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Video createVideofromInvidiousJSON(JSONObject obj){
|
||||||
|
Video video = null;
|
||||||
|
try{
|
||||||
|
String id = obj.getString("videoId");
|
||||||
|
String title = obj.getString("title");
|
||||||
|
title = Html.fromHtml(title).toString();
|
||||||
|
String author = obj.getString("author");
|
||||||
|
author = Html.fromHtml(author).toString();
|
||||||
|
|
||||||
|
String duration = formatIntegerDuration(obj.getInt("lengthSeconds"));
|
||||||
|
String thumb = "https://i.ytimg.com/vi/"+id+"/hqdefault.jpg";
|
||||||
|
|
||||||
|
|
||||||
|
String url = "https://www.youtube.com/watch?v=" + id;
|
||||||
|
int downloadedAudio = dbManager.checkDownloaded(url, "audio");
|
||||||
|
int downloadedVideo = dbManager.checkDownloaded(url, "video");
|
||||||
|
int isPlaylist = 0;
|
||||||
|
|
||||||
|
video = new Video(id, url, title, author, duration, thumb, downloadedAudio, downloadedVideo, isPlaylist, "youtube", 0, 0, "");
|
||||||
|
}catch(Exception e){
|
||||||
|
Log.e(TAG, e.toString());
|
||||||
|
}
|
||||||
|
return video;
|
||||||
|
}
|
||||||
|
|
||||||
private JSONObject genericRequest(String url){
|
private JSONObject genericRequest(String url){
|
||||||
Log.e(TAG, url);
|
Log.e(TAG, url);
|
||||||
|
|
||||||
|
|
@ -231,10 +301,39 @@ public class InfoUtil {
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Log.e(TAG, e.toString());
|
Log.e(TAG, e.toString());
|
||||||
}
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONArray genericArrayRequest(String url){
|
||||||
|
Log.e(TAG, url);
|
||||||
|
|
||||||
|
BufferedReader reader;
|
||||||
|
String line;
|
||||||
|
StringBuilder responseContent = new StringBuilder();
|
||||||
|
HttpURLConnection conn;
|
||||||
|
JSONArray json = new JSONArray();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
json = new JSONArray(responseContent.toString());
|
||||||
|
}
|
||||||
|
conn.disconnect();
|
||||||
|
}catch(Exception e){
|
||||||
|
Log.e(TAG, e.toString());
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,6 +363,66 @@ public class InfoUtil {
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ArrayList<Video> getTrending(Context context) throws JSONException{
|
||||||
|
videos = new ArrayList<>();
|
||||||
|
if (key.isEmpty()) {
|
||||||
|
if (useInvidous) return getTrendingFromInvidous(context);
|
||||||
|
else return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return getTrendingFromKey(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Video> getTrendingFromKey(Context context) throws JSONException{
|
||||||
|
String url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key;
|
||||||
|
//short data
|
||||||
|
JSONObject res = genericRequest(url);
|
||||||
|
//extra data from the same videos
|
||||||
|
JSONObject contentDetails = genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key);
|
||||||
|
|
||||||
|
if(!contentDetails.has("items")) return new ArrayList<>();
|
||||||
|
|
||||||
|
JSONArray dataArray = res.getJSONArray("items");
|
||||||
|
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
||||||
|
for(int i = 0; i < dataArray.length(); i++){
|
||||||
|
JSONObject element = dataArray.getJSONObject(i);
|
||||||
|
JSONObject snippet = element.getJSONObject("snippet");
|
||||||
|
|
||||||
|
String duration = extraDataArray.getJSONObject(i).getJSONObject("contentDetails").getString("duration");
|
||||||
|
duration = formatDuration(duration);
|
||||||
|
|
||||||
|
snippet.put("videoID", element.getString("id"));
|
||||||
|
snippet.put("duration", duration);
|
||||||
|
fixThumbnail(snippet);
|
||||||
|
|
||||||
|
Video v = createVideofromJSON(snippet);
|
||||||
|
if(v == null || v.getThumb().isEmpty()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
v.setPlaylistTitle(context.getString(R.string.trendingPlaylist));
|
||||||
|
videos.add(v);
|
||||||
|
}
|
||||||
|
return videos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Video> getTrendingFromInvidous(Context context) {
|
||||||
|
String url = invidousURL + "trending?type=music®ion="+countryCODE;
|
||||||
|
JSONArray res = genericArrayRequest(url);
|
||||||
|
try{
|
||||||
|
for(int i = 0; i < res.length(); i++){
|
||||||
|
JSONObject element = res.getJSONObject(i);
|
||||||
|
if(!element.getString("type").equals("video")) continue;
|
||||||
|
Video v = createVideofromInvidiousJSON(element);
|
||||||
|
if(v == null || v.getThumb().isEmpty()) continue;
|
||||||
|
v.setPlaylistTitle(context.getString(R.string.trendingPlaylist));
|
||||||
|
videos.add(v);
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return videos;
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayList<Video> getFromYTDL(String query){
|
public ArrayList<Video> getFromYTDL(String query){
|
||||||
videos = new ArrayList<>();
|
videos = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
|
|
@ -349,42 +508,26 @@ public class InfoUtil {
|
||||||
return videos;
|
return videos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Video> getTrending(Context context) throws JSONException{
|
|
||||||
if (key.isEmpty()) return new ArrayList<>();
|
|
||||||
|
|
||||||
videos = new ArrayList<>();
|
|
||||||
|
|
||||||
String url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key;
|
public ArrayList<String> getSearchSuggestions(String query){
|
||||||
//short data
|
if (!useInvidous) return new ArrayList<>();
|
||||||
|
String url = invidousURL + "search/suggestions?q="+query;
|
||||||
JSONObject res = genericRequest(url);
|
JSONObject res = genericRequest(url);
|
||||||
//extra data from the same videos
|
if(res.length() == 0) return new ArrayList<>();
|
||||||
JSONObject contentDetails = genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key);
|
|
||||||
|
|
||||||
if(!contentDetails.has("items")) return new ArrayList<>();
|
ArrayList<String> suggestionList = new ArrayList<>();
|
||||||
|
try{
|
||||||
JSONArray dataArray = res.getJSONArray("items");
|
JSONArray suggestions = res.getJSONArray("suggestions");
|
||||||
JSONArray extraDataArray = contentDetails.getJSONArray("items");
|
for (int i = 0; i < suggestions.length(); i++){
|
||||||
for(int i = 0; i < dataArray.length(); i++){
|
suggestionList.add(suggestions.getString(i));
|
||||||
JSONObject element = dataArray.getJSONObject(i);
|
|
||||||
JSONObject snippet = element.getJSONObject("snippet");
|
|
||||||
|
|
||||||
String duration = extraDataArray.getJSONObject(i).getJSONObject("contentDetails").getString("duration");
|
|
||||||
duration = formatDuration(duration);
|
|
||||||
|
|
||||||
snippet.put("videoID", element.getString("id"));
|
|
||||||
snippet.put("duration", duration);
|
|
||||||
snippet = fixThumbnail(snippet);
|
|
||||||
|
|
||||||
Video v = createVideofromJSON(snippet);
|
|
||||||
if(v == null || v.getThumb().isEmpty()){
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
v.setPlaylistTitle(context.getString(R.string.trendingPlaylist));
|
}catch(Exception ignored){}
|
||||||
videos.add(v);
|
|
||||||
}
|
return suggestionList;
|
||||||
return videos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String formatDuration(String dur){
|
public String formatDuration(String dur){
|
||||||
if(dur.equals("P0D")){
|
if(dur.equals("P0D")){
|
||||||
return "LIVE";
|
return "LIVE";
|
||||||
|
|
@ -426,45 +569,6 @@ public class InfoUtil {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 static class PlaylistTuple {
|
public static class PlaylistTuple {
|
||||||
String nextPageToken;
|
String nextPageToken;
|
||||||
ArrayList<Video> videos;
|
ArrayList<Video> videos;
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ public class NotificationUtil {
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||||
.clearActions()
|
.clearActions()
|
||||||
//.addAction(0, context.getString(R.string.cancel), cancelNotificationPendingIntent)
|
.addAction(0, context.getString(R.string.cancel), cancelNotificationPendingIntent)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
|
|
|
||||||
5
app/src/main/res/drawable/ic_arrow_up.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_up.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:textColorPrimary" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -27,6 +27,21 @@
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/search_suggestions_scroll_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/search_suggestions_linear_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include layout="@layout/search_suggestion_item" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
67
app/src/main/res/layout/search_suggestion_item.xml
Normal file
67
app/src/main/res/layout/search_suggestion_item.xml
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/search_suggestions_constraintLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/downloads_relative_layout"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintDimensionRatio="H,8:1"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/downloads_card_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:cardBackgroundColor="@android:color/transparent"
|
||||||
|
android:checkable="true"
|
||||||
|
app:strokeWidth="0dp"
|
||||||
|
app:cardPreventCornerOverlap="true"
|
||||||
|
android:layout_margin="10dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.01"
|
||||||
|
app:icon="@drawable/ic_search"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/suggestion_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingEnd="20dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:id="@+id/set_search_query_button"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:icon="@drawable/ic_arrow_up"
|
||||||
|
android:layout_weight="0.01"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Loading…
Reference in a new issue