首页接口解析

This commit is contained in:
litingting 2024-09-14 15:26:42 +08:00
parent d76003abef
commit 9a3baf1261
20 changed files with 621 additions and 20 deletions

View File

@ -4,14 +4,15 @@ plugins {
android {
namespace = "com.hi.music.player"
compileSdk = 33
compileSdk = 34
defaultConfig {
applicationId = "com.hi.music.player"
//com.hi.music.player
applicationId = "com.hi.music.player.test"
minSdk = 23
targetSdk = 33
targetSdk = 34
versionCode = 1
versionName = "1.0"
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@ -29,6 +30,9 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding = true
}
}
dependencies {
@ -39,4 +43,12 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.retrofit2:adapter-rxjava2:2.9.0")
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
implementation ("com.squareup.okhttp3:logging-interceptor:4.11.0")
}

View File

@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -12,8 +13,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.MusicApp"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -1,14 +0,0 @@
package com.hi.music.player;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

View File

@ -0,0 +1,13 @@
package com.hi.music.player;
import android.app.Application;
public class MusicApplication extends Application {
private final static MusicApplication sInstance = new MusicApplication();
public static MusicApplication getInstance() {
return sInstance;
}
}

View File

@ -0,0 +1,12 @@
package com.hi.music.player.api;
import com.hi.music.player.javabean.response.BaseResponse;
import java.io.IOException;
public interface RequestListener<T> {
void onFail(String errorMsg);
void onSuccess(T data) ;
}

View File

@ -0,0 +1,24 @@
package com.hi.music.player.helper;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
public class BaseViewModelStoreOwner implements ViewModelStoreOwner {
private final static BaseViewModelStoreOwner sInstance = new BaseViewModelStoreOwner();
private ViewModelStore mAppViewModelStore;
private BaseViewModelStoreOwner() {
}
public static BaseViewModelStoreOwner getInstance() {
return sInstance;
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mAppViewModelStore == null) mAppViewModelStore = new ViewModelStore();
return mAppViewModelStore;
}
}

View File

@ -0,0 +1,30 @@
package com.hi.music.player.helper;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.ResponseBody;
public class CommonUtils {
private static String TAG = "----MUSIC---------";
public static void LogMsg(String msg) {
Log.d(TAG, msg);
}
public static JSONObject toJsonObject(ResponseBody body) {
try {
String string = body.string();
return new JSONObject(string);
} catch (IOException | JSONException exception) {
return null;
}
}
}

View File

@ -0,0 +1,31 @@
package com.hi.music.player.helper;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.hi.music.player.MusicApplication;
public class ViewModelScope {
private ViewModelProvider mFragmentProvider;
private ViewModelProvider mActivityProvider;
private ViewModelProvider mApplicationProvider;
public <T extends ViewModel> T getFragmentScopeViewModel(@NonNull Fragment fragment, @NonNull Class<T> modelClass) {
if (mFragmentProvider == null) mFragmentProvider = new ViewModelProvider(fragment);
return mFragmentProvider.get(modelClass);
}
public <T extends ViewModel> T getActivityScopeViewModel(@NonNull AppCompatActivity activity, @NonNull Class<T> modelClass) {
if (mActivityProvider == null) mActivityProvider = new ViewModelProvider(activity);
return mActivityProvider.get(modelClass);
}
public <T extends ViewModel> T getApplicationScopeViewModel(@NonNull Class<T> modelClass) {
if (mApplicationProvider == null)
mApplicationProvider = new ViewModelProvider(BaseViewModelStoreOwner.getInstance());
return mApplicationProvider.get(modelClass);
}
}

View File

@ -0,0 +1,28 @@
package com.hi.music.player.javabean.requestbody;
import java.io.Serializable;
import java.util.Locale;
/**
* 首页接口请求体
*/
public class BodyHome implements Serializable {
private String browseId = "FEmusic_home";
private Context context = new Context();
private class Context implements Serializable{
private Client client = new Client();
}
private class Client implements Serializable{
private String clientName = "WEB_REMIX";
private String clientVersion = "1.20220918";
private String hl = Locale.getDefault().getLanguage();
private String gl = "US";
private String platform = "DESKTOP";
}
}

View File

@ -0,0 +1,32 @@
package com.hi.music.player.javabean.response;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.MediaType;
import okio.BufferedSource;
import retrofit2.Response;
public class BaseResponse {
// public JSONObject toJsonObject() {
// try {
// String string = this.string();
// return new JSONObject(string);
// } catch (IOException | JSONException exception) {
// return null;
// }
// }
}

View File

@ -0,0 +1,5 @@
package com.hi.music.player.javabean.response;
public class ResponseHome {
private String backgroundUrl;
}

View File

@ -0,0 +1,92 @@
package com.hi.music.player.network;
import com.hi.music.player.helper.CommonUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JsonHelper {
public static void ResolveHomeJson(JSONObject jsonObject){
try {
String bgUrl = jsonObject
.getJSONObject("background")
.getJSONObject("musicThumbnailRenderer")
.getJSONObject("thumbnail")
.getJSONArray("thumbnails")
.getJSONObject(0)
.getString("url");
JSONArray contents = jsonObject
.getJSONObject("contents")
.getJSONObject("singleColumnBrowseResultsRenderer")
.getJSONArray("tabs")
.getJSONObject(0)
.getJSONObject("tabRenderer")
.getJSONObject("content")
.getJSONObject("sectionListRenderer")
.getJSONArray("contents");
JSONObject responseContext = jsonObject
.getJSONObject("responseContext");
JSONArray serviceTrackingParams = responseContext.getJSONArray("serviceTrackingParams");
String visitorData = responseContext.getString("visitorData");
for (int i = 0; i < contents.length(); i++) {
JSONObject object = contents.getJSONObject(i);
JSONObject musicCarouselShelfRenderer = object.optJSONObject("musicCarouselShelfRenderer");
if(musicCarouselShelfRenderer!= null){
String title = musicCarouselShelfRenderer
.getJSONObject("header")
.getJSONObject("musicCarouselShelfBasicHeaderRenderer")
.getJSONObject("title")
.getJSONArray("runs")
.getJSONObject(0)
.getString("text");
CommonUtils.LogMsg("----------headertitle=" + title);
JSONArray childContents = musicCarouselShelfRenderer
.getJSONArray("contents");
for (int j = 0; j < childContents.length(); j++) {
JSONObject jsonList = childContents.getJSONObject(j);
JSONObject musicResponsiveListItemRenderer = jsonList.optJSONObject("musicResponsiveListItemRenderer");
if(musicResponsiveListItemRenderer!= null){
String covert = musicResponsiveListItemRenderer
.getJSONObject("thumbnail")
.getJSONObject("musicThumbnailRenderer")
.getJSONObject("thumbnail")
.getJSONArray("thumbnails")
.getJSONObject(0)
.getString("url");
String text = musicResponsiveListItemRenderer.getJSONArray("flexColumns")
.getJSONObject(0)
.getJSONObject("musicResponsiveListItemFlexColumnRenderer")
.getJSONObject("text")
.getJSONArray("runs")
.getJSONObject(0)
.getString("text");
CommonUtils.LogMsg(" --------------text=" + text+"-covert="+covert);
}
}
}
}
} catch (JSONException exception) {
CommonUtils.LogMsg("----------exception=");
exception.printStackTrace();
}
}
}

View File

@ -0,0 +1,20 @@
package com.hi.music.player.network;
import com.hi.music.player.javabean.response.BaseResponse;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface MusicApi {
@POST("youtubei/v1/browse?prettyPrint=false")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getHomeData(@Body RequestBody requestBody);
}

View File

@ -0,0 +1,39 @@
package com.hi.music.player.network;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.BaseResponse;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class ObserverWrapper<T> implements Observer<T> {
private RequestListener<T> requestListener;
public ObserverWrapper(RequestListener<T> requestListener) {
this.requestListener = requestListener;
}
@Override
public void onSubscribe(Disposable d) {
CommonUtils.LogMsg("----------onSubscribe");
}
@Override
public void onNext(T t) {
CommonUtils.LogMsg("----------onNext");
requestListener.onSuccess(t);
}
@Override
public void onError(Throwable e) {
CommonUtils.LogMsg("----------onError"+e.getMessage());
requestListener.onFail(e.getMessage());
}
@Override
public void onComplete() {
CommonUtils.LogMsg("----------onComplete");
}
}

View File

@ -0,0 +1,108 @@
package com.hi.music.player.network;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.requestbody.BodyHome;
import com.hi.music.player.javabean.response.BaseResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitManager {
private String base_Host = "https://music.youtube.com/";
private static volatile RetrofitManager REQUEST_MANAGER;
private Retrofit retrofit;
public MediaType JSON = MediaType.get("application/json; charset=utf-8");
private MusicApi musicApi;
private RetrofitManager() {
musicApi = getRetrofit().create(MusicApi.class);
}
// public <T> T getService(Class<T> cls){
// return getRetrofit().create(cls);
// }
private synchronized Retrofit getRetrofit() {
if (retrofit == null) {
long DEFAULT_TIMEOUT = 10;
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.addInterceptor(httpLoggingInterceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(base_Host)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
public static RetrofitManager getInstance() {
if (REQUEST_MANAGER == null) {
synchronized (RetrofitManager.class) { //,防止线程问题
if (REQUEST_MANAGER == null) {
REQUEST_MANAGER = new RetrofitManager();
}
}
}
return REQUEST_MANAGER;
}
public void getHomeData() {
BodyHome bodyHome = new BodyHome();
Gson gson = new Gson();
String s = gson.toJson(bodyHome);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
// RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), s);
musicApi.getHomeData(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<ResponseBody>(new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
}
@Override
public void onSuccess(ResponseBody data) {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
JsonHelper.ResolveHomeJson(jsonObject);
}
}
}));
}
}

View File

@ -0,0 +1,67 @@
package com.hi.music.player.ui;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import com.hi.music.player.helper.ViewModelScope;
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
private final ViewModelScope mViewModelScope = new ViewModelScope();
protected T vb;
private Window window;
private View decorView;
protected View mView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vb = getViewBinding();
setContentView(vb.getRoot());
window = getWindow();
decorView = window.getDecorView();
mView = decorView.getRootView();
setStatusBar();
if (isFullScreen())
initFullScreen();
onCreateInit();
}
protected abstract T getViewBinding();
protected abstract void onCreateInit();
public abstract boolean isFullScreen();
public abstract boolean statusBarLight();
private void setStatusBar() {
//深色模式
WindowInsetsControllerCompat insetsController = WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
insetsController.setAppearanceLightStatusBars(statusBarLight());
}
private void initFullScreen() {
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
protected <K extends ViewModel> K getActivityScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getActivityScopeViewModel(this, modelClass);
}
protected <K extends ViewModel> K getApplicationScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getApplicationScopeViewModel(modelClass);
}
}

View File

@ -0,0 +1,52 @@
package com.hi.music.player.ui;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import com.hi.music.player.helper.ViewModelScope;
public abstract class BaseFragment<T extends ViewBinding> extends Fragment {
private final ViewModelScope mViewModelScope = new ViewModelScope();
protected T Vb;
protected AppCompatActivity mActivity;
public BaseFragment() {
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mActivity = (AppCompatActivity) context;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Vb = getFragmentVb();
initView();
return Vb.getRoot();
}
protected abstract T getFragmentVb();
protected abstract void initView();
protected <K extends ViewModel> K getFragmentScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getFragmentScopeViewModel(this, modelClass);
}
protected <K extends ViewModel> K getActivityScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getActivityScopeViewModel(mActivity, modelClass);
}
protected <K extends ViewModel> K getApplicationScopeViewModel(@NonNull Class<K> modelClass) {
return mViewModelScope.getApplicationScopeViewModel(modelClass);
}
}

View File

@ -0,0 +1,38 @@
package com.hi.music.player.ui;
import android.view.View;
import com.hi.music.player.databinding.ActivityMainBinding;
import com.hi.music.player.network.RetrofitManager;
import com.hi.music.player.ui.BaseActivity;
public class MainActivity extends BaseActivity<ActivityMainBinding> {
// EdgeToEdge.enable(this);
@Override
protected ActivityMainBinding getViewBinding() {
return ActivityMainBinding.inflate(getLayoutInflater());
}
@Override
protected void onCreateInit() {
vb.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RetrofitManager.getInstance().getHomeData();
}
});
}
@Override
public boolean isFullScreen() {
return false;
}
@Override
public boolean statusBarLight() {
return false;
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.BaseActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,12 +4,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
tools:context=".ui.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/tv"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"