рдореИрдВ рдЬрд╣рд╛рдВ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рджреЗрдЦрддрд╛ рд╣реВрдВ рдФрд░ рд╕реБрдирддрд╛ рд╣реВрдВред Android рдРрдк рдореЗрдВ Chromecast рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдирд╛


рд╕рдбрд╝рдХ рдкрд░, рдореИрдВ рдЕрдХреНрд╕рд░ рд╕реНрдорд╛рд░реНрдЯрдлреЛрди рд╕реЗ рдСрдбрд┐рдпреЛ рдкреБрд╕реНрддрдХреЗрдВ рдФрд░ рдкреЙрдбрдХрд╛рд╕реНрдЯ рд╕реБрдирддрд╛ рд╣реВрдВред рдЬрдм рдореИрдВ рдШрд░ рдЬрд╛рддрд╛ рд╣реВрдВ, рддреЛ рдореИрдВ рдЙрдиреНрд╣реЗрдВ рдПрдВрдбреНрд░реЙрдЗрдб рдЯреАрд╡реА рдпрд╛ Google рд╣реЛрдо рдкрд░ рд╕реБрдирдирд╛ рдЬрд╛рд░реА рд░рдЦрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред рд▓реЗрдХрд┐рди рд╕рднреА рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рдФрд░ рдпрд╣ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реЛрдЧрд╛ред


рдкрд┐рдЫрд▓реЗ 3 рд╡рд░реНрд╖реЛрдВ рдореЗрдВ Google рдХреЗ рдЖрдВрдХрдбрд╝реЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдПрдВрдбреНрд░реЙрдЗрдб рдЯреАрд╡реА рдкрд░ рдЙрдкрдХрд░рдгреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ 4 рдЧреБрдирд╛ рдмрдврд╝ рдЧрдИ рд╣реИ, рдФрд░ рд╡рд┐рдирд┐рд░реНрдорд╛рдг рднрд╛рдЧреАрджрд╛рд░реЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдкрд╣рд▓реЗ рд╣реА рд╕реМ рд╕реЗ рдЕрдзрд┐рдХ рд╣реЛ рдЧрдИ рд╣реИ: "рд╕реНрдорд╛рд░реНрдЯ" рдЯреАрд╡реА, рд╕реНрдкреАрдХрд░, рд╕реЗрдЯ-рдЯреЙрдк рдмреЙрдХреНрд╕ред рдпреЗ рд╕рднреА Chromecast рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдмрд╛рдЬрд╛рд░ рдореЗрдВ рдЕрднреА рднреА рдХрдИ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╣реИрдВ рдЬреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЗрд╕рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдг рдХреА рдХрдореА рд░рдЦрддреЗ рд╣реИрдВред


рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдЦреЗрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдВрдбреНрд░реЙрдЗрдб рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рдЕрдкрдиреЗ рдЕрдиреБрднрд╡ рдХреЛ рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред


рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ


рдпрджрд┐ рдпрд╣ рдЖрдкрдХрд╛ рдкрд╣рд▓реА рдмрд╛рд░ "Chromecast" рд╢рдмреНрдж рд╕реБрди рд░рд╣рд╛ рд╣реИ, рддреЛ рдореИрдВ рдЖрдкрдХреЛ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рдмрддрд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реВрдВрдЧрд╛ред рдЙрдкрдпреЛрдЧ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ, рдпрд╣ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:


  1. рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдПрдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдпрд╛ рд╡реЗрдмрд╕рд╛рдЗрдЯ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕рдВрдЧреАрдд рд╕реБрдирддрд╛ рд╣реИ рдпрд╛ рд╡реАрдбрд┐рдпреЛ рджреЗрдЦрддрд╛ рд╣реИред
  2. Chromecast рдбрд┐рд╡рд╛рдЗрд╕ рд╕реНрдерд╛рдиреАрдп рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИред
  3. рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ рдЗрдВрдЯрд░рдлреЗрд╕ рдореЗрдВ рдПрдХ рд╕рдВрдмрдВрдзрд┐рдд рдмрдЯрди рджрд┐рдЦрд╛рдИ рджреЗрдирд╛ рдЪрд╛рд╣рд┐рдПред
  4. рдЙрд╕ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рдХреЗ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реВрдЪреА рд╕реЗ рд╡рд╛рдВрдЫрд┐рдд рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рдЪрдпрди рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдПрдХ рдиреЗрдХреНрд╕рд╕ рдкреНрд▓реЗрдпрд░, рдПрдВрдбреНрд░реЙрдЗрдб рдЯреАрд╡реА рдпрд╛ рдПрдХ рд╕реНрдорд╛рд░реНрдЯ рд╕реНрдкреАрдХрд░ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред
  5. рдЗрд╕ рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рд╕рд╛рде рдЖрдЧреЗ рдкреНрд▓реЗрдмреИрдХ рдЬрд╛рд░реА рд╣реИред


рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ, рдирд┐рдореНрди рдЬреИрд╕рд╛ рдХреБрдЫ рд╣реЛрддрд╛ рд╣реИ:


  1. Google рд╕реЗрд╡рд╛ рдкреНрд░рд╕рд╛рд░рдг рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдерд╛рдиреАрдп рдиреЗрдЯрд╡рд░реНрдХ рдкрд░ Chromecast рдЙрдкрдХрд░рдгреЛрдВ рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░рддреА рд╣реИред
  2. рдпрджрд┐ MediaRouter рдЖрдкрдХреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕реЗ рдЬреБрдбрд╝рд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рдШрдЯрдирд╛ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдЧреАред
  3. рдЬрдм рдХреЛрдИ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдПрдХ рдХрд╛рд╕реНрдЯ рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рдЪрдпрди рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЙрд╕рд╕реЗ рдХрдиреЗрдХреНрдЯ рд╣реЛрддрд╛ рд╣реИ, рддреЛ рдПрдХ рдирдпрд╛ рдореАрдбрд┐рдпрд╛ рд╕рддреНрд░ (CastSession) рдЦреБрд▓рддрд╛ рд╣реИред
  4. рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдмрдирд╛рдП рдЧрдП рд╕рддреНрд░ рдореЗрдВ, рд╣рдо рдкреНрд▓реЗрдмреИрдХ рдХреЗ рд▓рд┐рдП рд╕рд╛рдордЧреНрд░реА рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░реЗрдВрдЧреЗред
    рдпрд╣ рдмрд╣реБрдд рд╕рд░рд▓ рд▓рдЧрддрд╛ рд╣реИред

рдПрдХреАрдХрд░рдг


Google рдХреЗ рдкрд╛рд╕ рдЕрдкрдирд╛ Chromecast SDK рд╣реИ , рд▓реЗрдХрд┐рди рдпрд╣ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдореЗрдВ рдЦрд░рд╛рдм рд░реВрдк рд╕реЗ рдХрд╡рд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдЗрд╕рдХреЗ рдХреЛрдб рдХреЛ рдмрд╛рдзрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдХрдИ рдЪреАрдЬреЛрдВ рдХреЛ рдЯрд╛рдЗрдк рдХрд░рдХреЗ рдЬрд╛рдВрдЪрдирд╛ рдкрдбрд╝рддрд╛ рдерд╛ред рдЪрд▓реЛ рд╕рдм рдХреБрдЫ рдХреНрд░рдо рдореЗрдВ рдорд┐рд▓рддрд╛ рд╣реИред


рдкреНрд░рд╛рд░рдВрдн


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рд╣рдореЗрдВ рдХрд╛рд╕реНрдЯ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдлреНрд░реЗрдорд╡рд░реНрдХ рдФрд░ рдореАрдбрд┐рдпрд╛ рд░рд╛рдЙрдЯрд░ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:


implementation "com.google.android.gms:play-services-cast-framework:16.1.0" implementation "androidx.mediarouter:mediarouter:1.0.0" 

рдлрд┐рд░ рдХрд╛рд╕реНрдЯ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ (рдЙрд╕ рдкрд░ рдмрд╛рдж рдореЗрдВ), рдФрд░ рд╕рдорд░реНрдерд┐рдд рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХреЗ рдкреНрд░рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред рдпрд╣реА рд╣реИ, рдЕрдЧрд░ рд╣рдорд╛рд░рд╛ рдЖрд╡реЗрджрди рдХреЗрд╡рд▓ рд╡реАрдбрд┐рдпреЛ рдЪрд▓рд╛рддрд╛ рд╣реИ, рддреЛ Google рд╣реЛрдо рдХреЙрд▓рдо рдкрд░ рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реЛрдЧрд╛, рдФрд░ рдпрд╣ рдЙрдкрдХрд░рдгреЛрдВ рдХреА рд╕реВрдЪреА рдореЗрдВ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, OptionsProvider рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрдирд╛рдПрдВ:


 class CastOptionsProvider: OptionsProvider { override fun getCastOptions(context: Context): CastOptions { return CastOptions.Builder() .setReceiverApplicationId(BuildConfig.CHROMECAST_APP_ID) .build() } override fun getAdditionalSessionProviders(context: Context): MutableList<SessionProvider>? { return null } } 

рдФрд░ рдЗрд╕реЗ рдШреЛрд╖рдгрд╛рдкрддреНрд░ рдореЗрдВ рдШреЛрд╖рд┐рдд рдХрд░реЗрдВ:


 <meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:value="your.app.package.CastOptionsProvider" /> 

рдЖрд╡реЗрджрди рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░реЗрдВ


Chromecast рдХреЛ рд╣рдорд╛рд░реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЗрд╕реЗ Google рдХрд╛рд╕реНрдЯ рдПрд╕рдбреАрдХреЗ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХрдВрд╕реЛрд▓ рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рдЗрд╕рдХреЗ рд▓рд┐рдП Chromecast рдбреЗрд╡рд▓рдкрд░ рдЦрд╛рддреЗ (Google Play рдбреЗрд╡рд▓рдкрд░ рдЦрд╛рддреЗ рдХреЗ рд╕рд╛рде рднреНрд░рдорд┐рдд рди рд╣реЛрдирд╛) рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдкрдВрдЬреАрдХрд░рдг рдХрд░рддреЗ рд╕рдордп, рдЖрдкрдХреЛ $ 5 рдХрд╛ рдПрдХ рдмрд╛рд░ рд╢реБрд▓реНрдХ рджреЗрдирд╛ рд╣реЛрдЧрд╛ред рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдЖрдкрдХреЛ рдереЛрдбрд╝рд╛ рдЗрдВрддрдЬрд╛рд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рдХрдВрд╕реЛрд▓ рдореЗрдВ, рдЖрдк рд╕реНрдХреНрд░реАрди рдХреЗ рд╕рд╛рде рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рд▓рд┐рдП рдХрд╛рд╕реНрдЯ рдкреНрд▓реЗрдпрд░ рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдХреЛ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рднреАрддрд░ рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рдПрдирд╛рд▓рд┐рдЯрд┐рдХреНрд╕ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред


рдореАрдбрд┐рдпрд╛ рд░рд╛рдЙрдЯрд░


MediaRouteFramework рдПрдХ рддрдВрддреНрд░ рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдкрд╛рд╕ рд╕рднреА рджреВрд░рд╕реНрде рдкреНрд▓реЗрдмреИрдХ рдЙрдкрдХрд░рдгреЛрдВ рдХреЛ рдЦреЛрдЬрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдпрд╣ рди рдХреЗрд╡рд▓ рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдмрд▓реНрдХрд┐ рддреАрд╕рд░реЗ рдкрдХреНрд╖ рдХреЗ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджреВрд░рд╕реНрде рдбрд┐рд╕реНрдкреНрд▓реЗ рдФрд░ рд╕реНрдкреАрдХрд░ рднреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реЗ рдкрд╛рд╕ Chromecast рдХреА рдХрд┐рддрдиреА рд░реБрдЪрд┐ рд╣реИред



MediaRouteFramework рдореЗрдВ рдПрдХ рджреГрд╢реНрдп рд╣реИ рдЬреЛ рдореАрдбрд┐рдпрд╛ рд╕реНрдХреВрдЯрд░ рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рджрд░реНрд╢рд╛рддрд╛ рд╣реИред рдЗрд╕реЗ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рджреЛ рддрд░реАрдХреЗ рд╣реИрдВ:


1) рдореЗрдиреВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ:


 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> ... <item android:id="@+id/menu_media_route" android:title="@string/cast" app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider" app:showAsAction="always"/> ... </menu> 

2) рд╡рд╛рдпрд╛ рд▓реЗрдЖрдЙрдЯ:


 <androidx.mediarouter.app.MediaRouteButton android:id="@+id/mediaRouteButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:mediaRouteTypes="user"/> 

рдФрд░ рдХреЛрдб рд╕реЗ рдЖрдкрдХреЛ рдмрд╕ CastButtonFactory рдореЗрдВ рдмрдЯрди рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рддрдм рдореАрдбрд┐рдпрд╛ рд╕реНрдХреВрдЯрд░ рдХреА рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдХреЛ рдЗрд╕рдореЗрдВ рдбрд╛рд▓рд╛ рдЬрд╛рдПрдЧрд╛:


 CastButtonFactory.setUpMediaRouteButton(applicationContext, view.mediaRouteButton) 

рдЕрдм рдЬрдм рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкрдВрдЬреАрдХреГрдд рд╣реЛ рдЧрдпрд╛ рд╣реИ рдФрд░ MediaRouter рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рдЖрдк рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рдбрд┐рд╡рд╛рдЗрд╕реЛрдВ рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рддреНрд░ рдЦреЛрд▓ рд╕рдХрддреЗ рд╣реИрдВред


рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХрд╛рд╕реНрдЯрд┐рдВрдЧ


рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рддреАрди рдореБрдЦреНрдп рдкреНрд░рдХрд╛рд░ рдХреА рд╕рд╛рдордЧреНрд░реА рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ:


  • рдСрдбрд┐рдпреЛ;
  • рд╡реАрдбрд┐рдпреЛ;
  • рдлреЛрдЯреЛред

рдЦрд┐рд▓рд╛рдбрд╝реА рдХреА рд╕реЗрдЯрд┐рдВрдЧреНрд╕, рдЬреИрд╕реЗ рдХрд┐ рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдФрд░ рдХрд╛рд╕реНрдЯ рдбрд┐рд╡рд╛рдЗрд╕ рдХреЗ рдЖрдзрд╛рд░ рдкрд░, рдЦрд┐рд▓рд╛рдбрд╝реА рдХрд╛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИред


CastSession


рдЗрд╕рд▓рд┐рдП, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдиреЗ рд╡рд╛рдВрдЫрд┐рдд рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рдЪрдпрди рдХрд┐рдпрд╛, CastFramework рдиреЗ рдПрдХ рдирдпрд╛ рд╕рддреНрд░ рдЦреЛрд▓рд╛ред рдЕрдм рд╣рдорд╛рд░рд╛ рдХрд╛рдо рдЗрд╕ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рджреЗрдирд╛ рдФрд░ рдкреНрд▓реЗрдмреИрдХ рдХреЗ рд▓рд┐рдП рдбрд┐рд╡рд╛рдЗрд╕ рдХреЛ рдЬрд╛рдирдХрд╛рд░реА рджреЗрдирд╛ рд╣реИред
рд╕рддреНрд░ рдХреА рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдФрд░ рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд╛рдЗрди рдЕрдк рдХрд░реЗрдВ, SessionManager рдСрдмреНрдЬреЗрдХреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:


 private val mediaSessionListener = object : SessionManagerListener<CastSession> { override fun onSessionStarted(session: CastSession, sessionId: String) { currentSession = session //  ,      checkAndStartCasting() } override fun onSessionEnding(session: CastSession) { stopCasting() } override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { currentSession = session checkAndStartCasting() } override fun onSessionStartFailed(session: CastSession, p1: Int) { stopCasting() } override fun onSessionEnded(session: CastSession, p1: Int) { // do nothing } override fun onSessionResumeFailed(session: CastSession, p1: Int) { // do nothing } override fun onSessionSuspended(session: CastSession, p1: Int) { // do nothing } override fun onSessionStarting(session: CastSession) { // do nothing } override fun onSessionResuming(session: CastSession, sessionId: String) { // do nothing } } val sessionManager = CastContext.getSharedInstance(context).sessionManager sessionManager.addSessionManagerListener(mediaSessionListener, CastSession::class.java) 

рдФрд░ рд╣рдо рдпрд╣ рднреА рдкрддрд╛ рд▓рдЧрд╛ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдЗрд╕ рд╕рдордп рдПрдХ рдЦреБрд▓рд╛ рд╕рддреНрд░ рд╣реИ:


 val currentSession: CastSession? = sessionManager.currentCastSession 

рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рджреЛ рдореБрдЦреНрдп рд╢рд░реНрддреЗрдВ рд╣реИрдВ рдЬрд┐рдирдХреЗ рддрд╣рдд рд╣рдо рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рд╢реБрд░реВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


  1. рд╕рддреНрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЦреБрд▓рд╛ рд╣реИред
  2. рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рд╕рд╛рдордЧреНрд░реА рд╣реИред

рдЗрди рджреЛ рдШрдЯрдирд╛рдУрдВ рдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдкрд░, рд╣рдо рд╕реНрдерд┐рддрд┐ рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдпрджрд┐ рд╕рдм рдХреБрдЫ рдХреНрд░рдо рдореЗрдВ рд╣реИ, рддреЛ рдХрд╛рд╕реНрдЯрд┐рдВрдЧ рд╢реБрд░реВ рдХрд░реЗрдВред


рдврд▓рд╛рдИ


рдЕрдм рдЬрдм рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреНрдпрд╛ рдбрд╛рд▓рдирд╛ рд╣реИ рдФрд░ рдХрд╣рд╛рдВ рдбрд╛рд▓рдирд╛ рд╣реИ, рд╣рдо рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдкрд░ рдЖрдЧреЗ рдмрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред рдЕрдиреНрдп рдмрд╛рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, CastSession рдореЗрдВ рдПрдХ RemoteMediaClient рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИ рдЬреЛ рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХреА рдкреНрд▓реЗрдмреИрдХ рд╕реНрдерд┐рддрд┐ рдХреЗ рд▓рд┐рдП рдЬрд╝рд┐рдореНрдореЗрджрд╛рд░ рд╣реИред рд╣рдо рдЙрд╕рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░реЗрдВрдЧреЗред


рдЪрд▓рд┐рдП MediaMetadata рдмрдирд╛рддреЗ рд╣реИрдВ, рдЬрд╣рд╛рдБ рд▓реЗрдЦрдХ, рдПрд▓реНрдмрдо, рдЖрджрд┐ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╕рдВрдЧреНрд░рд╣реАрдд рдХреА рдЬрд╛рдПрдЧреАред рдпрд╣ рдмрд╣реБрдд рд╣рдж рддрдХ рд╡реИрд╕рд╛ рд╣реА рд╣реИ рдЬреИрд╕рд╛ рд╣рдо рд╕реНрдерд╛рдиреАрдп рдкреНрд▓реЗрдмреИрдХ рд╢реБрд░реВ рдХрд░рдиреЗ рдкрд░ MediaSession рдХреЛ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред


 val mediaMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK ).apply { putString(MediaMetadata.KEY_TITLE, тАЬIn CтАЭ) putString(MediaMetadata.KEY_ARTIST, тАЬTerry RileyтАЭ) mediaContent?.metadata?.posterUrl?.let { poster -> addImage(WebImage(Uri.parse(тАЬhttps://habrastorage.org/webt/wk/oi/pf/wkoipfkdyy2ctoa5evnd8vhxtem.pngтАЭ))) } } 

MediaMetadata рдореЗрдВ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИрдВ, рдФрд░ рдЙрдиреНрд╣реЗрдВ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рджреЗрдЦрдирд╛ рдмреЗрд╣рддрд░ рд╣реИред рдореБрдЭреЗ рд╕реБрдЦрдж рдЖрд╢реНрдЪрд░реНрдп рд╣реБрдЖ рдХрд┐ рдЖрдк рдмрд┐рдЯрдореИрдк рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ WebImage рдХреЗ рдЕрдВрджрд░ рд▓рд┐рдВрдХ рджреНрд╡рд╛рд░рд╛ рдПрдХ рдЫрд╡рд┐ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред


MediaInfo рдСрдмреНрдЬреЗрдХреНрдЯ рд╕рд╛рдордЧреНрд░реА рдореЗрдЯрд╛рдбреЗрдЯрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд░рдЦрддрд╛ рд╣реИ рдФрд░ рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдЧрд╛ рдХрд┐ рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХрд╣рд╛рдБ рд╕реЗ рдЖрддреА рд╣реИ, рдпрд╣ рдХрд┐рд╕ рдкреНрд░рдХрд╛рд░ рдХреА рд╣реИ, рдЗрд╕реЗ рдХреИрд╕реЗ рдЦреЗрд▓реЗрдВ:


 val mediaInfo = MediaInfo.Builder(тАЬhttps://you-address.com/in_c.mp3тАЭ) .setContentType(тАЬaudio/mp3тАЭ) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setMetadata(mediaMetadata) .build() 

рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛ рджреВрдВ рдХрд┐ MIME рд╡рд┐рдирд┐рд░реНрджреЗрд╢рди рдХреЗ рдЕрдиреБрд╕рд╛рд░ contentType рдПрдХ рдкреНрд░рдХрд╛рд░ рдХреА рд╕рд╛рдордЧреНрд░реА рд╣реИред
рдЖрдк рд╡рд┐рдЬреНрдЮрд╛рдкрди рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреЛ MediaInfo рдореЗрдВ рднреА рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


  • setAdBreakClips - AdBreakClipInfo рд╡рд┐рдЬреНрдЮрд╛рдкрдиреЛрдВ рдХреА рд╕реВрдЪреА рдХреЛ рд╕рд╛рдордЧреНрд░реА, рд╢реАрд░реНрд╖рдХ, рд╕рдордп рдФрд░ рдЙрд╕ рд▓рд┐рдВрдХ рдХреЗ рд╕рд╛рде рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╡рд┐рдЬреНрдЮрд╛рдкрди рдЫреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
  • setAdBreaks - рд╡рд┐рдЬреНрдЮрд╛рдкрди рдЖрд╡реЗрд╖рдг рдХреЗ рд▓реЗрдЖрдЙрдЯ рдФрд░ рд╕рдордп рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реАред

MediaLoadOptions рдореЗрдВ рд╣рдо рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдореАрдбрд┐рдпрд╛ рд╕реНрдЯреНрд░реАрдо (рдЧрддрд┐, рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реНрдерд┐рддрд┐) рдХреЛ рдХреИрд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░реЗрдВрдЧреЗред рдкреНрд░рд▓реЗрдЦрди рдпрд╣ рднреА рдХрд╣рддрд╛ рд╣реИ рдХрд┐ setCredentials рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЖрдк рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рд╢реАрд░реНрд╖ рд▓реЗрдЦ рдкрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди Chromecast рдХреЗ рдореЗрд░реЗ рдЕрдиреБрд░реЛрдзреЛрдВ рдореЗрдВ рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рд▓рд┐рдП рдШреЛрд╖рд┐рдд рдлрд╝реАрд▓реНрдб рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рдереЗред


 val mediaLoadOptions = MediaLoadOptions.Builder() .setPlayPosition(position!!) .setAutoplay(true) .setPlaybackRate(playbackSpeed) .setCredentials(context.getString(R.string.bearer_token, authGateway.authState.accessToken!!)) .setCredentialsType(context.getString(R.string.authorization_header_key)) .build() 

рдПрдХ рдмрд╛рд░ рд╕рдм рдХреБрдЫ рддреИрдпрд╛рд░ рд╣реЛ рдЬрд╛рдиреЗ рдкрд░, рд╣рдо рд╕рднреА рдбреЗрдЯрд╛ рдХреЛ RemoteMediaClient рдХреЛ рджреЗ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ Chromecast рдЦреЗрд▓рдирд╛ рд╢реБрд░реВ рдХрд░ рджреЗрдЧрд╛ред рд╕реНрдерд╛рдиреАрдп рдкреНрд▓реЗрдмреИрдХ рдХреЛ рд░реЛрдХрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред


 val remoteMediaClient = currentSession!!.remoteMediaClient remoteMediaClient.load(mediaInfo, mediaLoadOptions) 

рдЗрд╡реЗрдВрдЯ рд╣реИрдВрдбрд▓рд┐рдВрдЧ


рд╡реАрдбрд┐рдпреЛ рдЪрд▓рдирд╛ рд╢реБрд░реВ рд╣реБрдЖ рдФрд░ рдлрд┐рд░ рдХреНрдпрд╛? рдпрджрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЯреАрд╡реА рдХреЛ рд░реЛрдХ рджреЗрддрд╛ рд╣реИ рддреЛ рдХреНрдпрд╛ рд╣реЛрдЧрд╛? Chromecast рдХреА рдУрд░ рд╕реЗ рдШрдЯрдирд╛рдУрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП, RemoteMediaClient рдореЗрдВ рдХреЙрд▓рдмреИрдХ рд╣реИрдВ:


 private val castStatusCallback = object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { // check and update current state } } remoteMediaClient.registerCallback(castStatusCallback) 

рд╡рд░реНрддрдорд╛рди рдкреНрд░рдЧрддрд┐ рдХреЛ рдЬрд╛рдирдирд╛ рднреА рд╕рд░рд▓ рд╣реИ:


 val periodMills = 1000L remoteMediaClient.addProgressListener( RemoteMediaClient.ProgressListener { progressMills, durationMills -> // show progress in your UI }, periodMills ) 

рдореМрдЬреВрджрд╛ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдг рдХрд╛ рдЕрдиреБрднрд╡


рдЬрд┐рд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкрд░ рдореИрдВ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рдерд╛, рдЙрд╕рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рддреИрдпрд╛рд░ рдореАрдбрд┐рдпрд╛ рдкреНрд▓реЗрдпрд░ рдерд╛ред рд▓рдХреНрд╖реНрдп рдЗрд╕рдореЗрдВ Chromecast рд╕рдорд░реНрдерди рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдирд╛ рдерд╛ред рдореАрдбрд┐рдпрд╛ рдкреНрд▓реЗрдпрд░ рд╕реНрдЯреЗрдЯ рдорд╢реАрди рдкрд░ рдЖрдзрд╛рд░рд┐рдд рдерд╛, рдФрд░ рдкрд╣рд▓рд╛ рд╡рд┐рдЪрд╛рд░ рдПрдХ рдирдП рд░рд╛рдЬреНрдп рдХреЛ рдЬреЛрдбрд╝рдирд╛ рдерд╛: "рдХрд╛рд╕реНрдЯрд┐рдВрдЧрд╕реНрдЯреИрдЯ"ред рд▓реЗрдХрд┐рди рдЗрд╕ рд╡рд┐рдЪрд╛рд░ рдХреЛ рддреБрд░рдВрдд рдЕрд╕реНрд╡реАрдХрд╛рд░ рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдХреНрдпреЛрдВрдХрд┐ рдкреНрд░рддреНрдпреЗрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рд░рд╛рдЬреНрдп рдкреНрд▓реЗрдмреИрдХ рд╕реНрдерд┐рддрд┐ рдХреЛ рджрд░реНрд╢рд╛рддрд╛ рд╣реИ, рдФрд░ рдЗрд╕рд╕реЗ рдХреЛрдИ рдлрд░реНрдХ рдирд╣реАрдВ рдкрдбрд╝рддрд╛ рдХрд┐ ExoPlayer рдпрд╛ ChromeCast рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд░реВрдк рдореЗрдВ рдХреНрдпрд╛ рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИред
рдлрд┐рд░ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ "рдЬреАрд╡рди рдЪрдХреНрд░" рдХреА рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рдФрд░ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдирд┐рдзрд┐рдпреЛрдВ рдХреА рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкреНрд░рдгрд╛рд▓реА рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рдЪрд╛рд░ рдЖрдпрд╛ред рд╕рднреА рдкреНрд░рддрд┐рдирд┐рдзрд┐рдпреЛрдВ рдХреЛ рдЦрд┐рд▓рд╛рдбрд╝реА рдХрд╛ рджрд░реНрдЬрд╛ рдкреНрд░рд╛рдкреНрдд рд╣реЛ рд╕рдХрддрд╛ рд╣реИ: рдкреНрд▓реЗ, рдкреЙрдЬрд╝ рдЖрджрд┐ред - рд▓реЗрдХрд┐рди рдХреЗрд╡рд▓ рдкреНрд░рдореБрдЦ рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХреЛ рдЦреЗрд▓реЗрдВрдЧреЗред



рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЗрд╕ рдЦрд┐рд▓рд╛рдбрд╝реА рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдЬреИрд╕рд╛ рдХреБрдЫ рд╣реИ:


 interface Player { val isPlaying: Boolean val isReleased: Boolean val duration: Long var positionInMillis: Long var speed: Float var volume: Float var loop: Boolean fun addListener(listener: PlayerCallback) fun removeListener(listener: PlayerCallback): Boolean fun getListeners(): MutableSet<PlayerCallback> fun prepare(mediaContent: MediaContent) fun play() fun pause() fun release() interface PlayerCallback { fun onPlaying(currentPosition: Long) fun onPaused(currentPosition: Long) fun onPreparing() fun onPrepared() fun onLoadingChanged(isLoading: Boolean) fun onDurationChanged(duration: Long) fun onSetSpeed(speed: Float) fun onSeekTo(fromTimeInMillis: Long, toTimeInMillis: Long) fun onWaitingForNetwork() fun onError(error: String?) fun onReleased() fun onPlayerProgress(currentPosition: Long) } } 

рдЗрд╕рдХреЗ рдЕрдВрджрд░ рдЗрддрдиреЗ рд╕рд╛рд░реЗ рд░рд╛рдЬреНрдпреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд╕реНрдЯреЗрдЯ рдорд╢реАрди рд╣реЛрдЧреА:


  • рдЦрд╛рд▓реА - рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЕрд╡рд╕реНрдерд╛ рдореЗрдВ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╣реЛрдиреЗ рд╕реЗ рдкрд╣рд▓реЗред
  • рддреИрдпрд╛рд░реА - рдЦрд┐рд▓рд╛рдбрд╝реА рдореАрдбрд┐рдпрд╛ рд╕рд╛рдордЧреНрд░реА рдХреЗ рдкреНрд▓реЗрдмреИрдХ рдХреА рд╢реБрд░реБрдЖрдд рдХрд░рддрд╛ рд╣реИред
  • рддреИрдпрд╛рд░ - рдореАрдбрд┐рдпрд╛ рдЕрдкрд▓реЛрдб рд╣реИ рдФрд░ рдЦреЗрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ рд╣реИред
  • рдмрдЬрд╛рдирд╛
  • рд░реЛрдХрд╛ рдЧрдпрд╛
  • рдкреНрд░рддреАрдХреНрд╖рд╛рд░рдд рдиреЗрдЯрд╡рд░реНрдХ
  • рддреНрд░реБрдЯрд┐


рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ, рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдЕрд╡рд╕реНрдерд╛ рдХреЗ рджреМрд░рд╛рди рдкреНрд░рддреНрдпреЗрдХ рд░рд╛рдЬреНрдп рдиреЗ рдПрдХреНрд╕реЛрдкреНрд▓реЗрдпрд░ рдореЗрдВ рдПрдХ рдЖрджреЗрд╢ рдЬрд╛рд░реА рдХрд┐рдпрд╛ рдерд╛ред рдЕрдм рдпрд╣ рдЦреЗрд▓рдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рддрд┐рдирд┐рдзрд┐рдпреЛрдВ рдХреА рд╕реВрдЪреА рдХреЛ рдХрдорд╛рдВрдб рдЬрд╛рд░реА рдХрд░реЗрдЧрд╛, рдФрд░ "рд▓реАрдб" рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдЗрд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдЧрд╛ред рдЪреВрдВрдХрд┐ рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ рд╕рднреА рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕реЗ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╕реЗ рднреА рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рд▓рд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рддреЛ рдЕрд▓рдЧ рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рддрдм рд╕рд╛рд░ рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:


 abstract class PlayingDelegate( protected val playerCallback: Player.PlayerCallback, var isLeading: Boolean = false ) : Player { fun setIsLeading(isLeading: Boolean, positionMills: Long, isPlaying: Boolean) { this.isLeading = isLeading if (isLeading) { onLeading(positionMills, isPlaying) } else { onDormant() } } final override fun addListener(listener: Player.PlayerCallback) { // do nothing } final override fun removeListener(listener: Player.PlayerCallback): Boolean { return false } final override fun getListeners(): MutableSet<Player.PlayerCallback> { return mutableSetOf() } /** *    */ open fun netwarkIsRestored() { // do nothing } /** *      */ abstract fun onLeading(positionMills: Long, isPlaying: Boolean) /** *      */ abstract fun onIdle() /** *     . *      , *       . */ abstract fun readyForLeading(): Boolean } 

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЛ рд╕рд░рд▓ рдмрдирд╛рдпрд╛ред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдХреБрдЫ рдФрд░ рдШрдЯрдирд╛рдПрдБ рд╣реИрдВред
рдкреНрд░рдЬрдирди рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдХрдИ рдкреНрд░рддрд┐рдирд┐рдзрд┐ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред Chromecast рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:


ChromeCastDelegate.kt
 class ChromeCastDelegate( private val context: Context, private val castCallback: ChromeCastListener, playerCallback: Player.PlayerCallback ) : PlayingDelegate(playerCallback) { companion object { private const val CONTENT_TYPE_VIDEO = "videos/mp4" private const val CONTENT_TYPE_AUDIO = "audio/mp3" private const val PROGRESS_DELAY_MILLS = 500L } interface ChromeCastListener { fun onCastStarted() fun onCastStopped() } private var sessionManager: SessionManager? = null private var currentSession: CastSession? = null private var mediaContent: MediaContent? = null private var currentPosition: Long = 0 private val mediaSessionListener = object : SessionManagerListener<CastSession> { override fun onSessionStarted(session: CastSession, sessionId: String) { currentSession = session castCallback.onCastStarted() } override fun onSessionEnding(session: CastSession) { currentPosition = session.remoteMediaClient?.approximateStreamPosition ?: currentPosition stopCasting() } override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { currentSession = session castCallback.onCastStarted() } override fun onSessionStartFailed(session: CastSession, p1: Int) { stopCasting() } override fun onSessionEnded(session: CastSession, p1: Int) { // do nothing } override fun onSessionResumeFailed(session: CastSession, p1: Int) { // do nothing } override fun onSessionSuspended(session: CastSession, p1: Int) { // do nothing } override fun onSessionStarting(session: CastSession) { // do nothing } override fun onSessionResuming(session: CastSession, sessionId: String) { // do nothing } } private val castStatusCallback = object : RemoteMediaClient.Callback() { override fun onStatusUpdated() { if (currentSession == null) return val playerState = currentSession!!.remoteMediaClient.playerState when (playerState) { MediaStatus.PLAYER_STATE_PLAYING -> playerCallback.onPlaying(positionInMillis) MediaStatus.PLAYER_STATE_PAUSED -> playerCallback.onPaused(positionInMillis) } } } private val progressListener = RemoteMediaClient.ProgressListener { progressMs, durationMs -> playerCallback.onPlayerProgress(progressMs) } // Playing delegate override val isReleased: Boolean = false override var loop: Boolean = false override val isPlaying: Boolean get() = currentSession?.remoteMediaClient?.isPlaying ?: false override val duration: Long get() = currentSession?.remoteMediaClient?.streamDuration ?: 0 override var positionInMillis: Long get() { currentPosition = currentSession?.remoteMediaClient?.approximateStreamPosition ?: currentPosition return currentPosition } set(value) { currentPosition = value checkAndStartCasting() } override var speed: Float = SpeedProvider.default() set(value) { field = value checkAndStartCasting() } override var volume: Float get() = currentSession?.volume?.toFloat() ?: 0F set(value) { currentSession?.volume = value.toDouble() } override fun prepare(mediaContent: MediaContent) { sessionManager = CastContext.getSharedInstance(context).sessionManager sessionManager?.addSessionManagerListener(mediaSessionListener, CastSession::class.java) currentSession = sessionManager?.currentCastSession this.mediaContent = mediaContent playerCallback.onPrepared() } override fun play() { if (isLeading) { currentSession?.remoteMediaClient?.play() } } override fun pause() { if (isLeading) { currentSession?.remoteMediaClient?.pause() } } override fun release() { stopCasting(true) } override fun onLeading(positionMills: Long, isPlaying: Boolean) { currentPosition = positionMills checkAndStartCasting() } override fun onIdle() { // TODO } override fun readyForLeading(): Boolean { return currentSession != null } // internal private fun checkAndStartCasting() { if (currentSession != null && mediaContent?.metadata != null && isLeading) { val mediaMetadata = MediaMetadata(getMetadataType(mediaContent!!.type)).apply { putString(MediaMetadata.KEY_TITLE, mediaContent?.metadata?.title.orEmpty()) putString(MediaMetadata.KEY_ARTIST, mediaContent?.metadata?.author.orEmpty()) mediaContent?.metadata?.posterUrl?.let { poster -> addImage(WebImage(Uri.parse(poster))) } } val mediaInfo = MediaInfo.Builder(mediaContent!!.contentUri.toString()) .setContentType(getContentType(mediaContent!!.type)) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) .setMetadata(mediaMetadata) .build() val mediaLoadOptions = MediaLoadOptions.Builder() .setPlayPosition(currentPosition) .setAutoplay(true) .setPlaybackRate(speed.toDouble()) .build() val remoteMediaClient = currentSession!!.remoteMediaClient remoteMediaClient.unregisterCallback(castStatusCallback) remoteMediaClient.load(mediaInfo, mediaLoadOptions) remoteMediaClient.registerCallback(castStatusCallback) remoteMediaClient.addProgressListener(progressListener, PROGRESS_DELAY_MILLS) } } private fun stopCasting(removeListener: Boolean = false) { if (removeListener) { sessionManager?.removeSessionManagerListener(mediaSessionListener, CastSession::class.java) } currentSession?.remoteMediaClient?.unregisterCallback(castStatusCallback) currentSession?.remoteMediaClient?.removeProgressListener(progressListener) currentSession?.remoteMediaClient?.stop() currentSession = null if (isLeading) { castCallback.onCastStopped() } } private fun getContentType(mediaType: MediaContent.Type) = when (mediaType) { MediaContent.Type.AUDIO -> CONTENT_TYPE_AUDIO MediaContent.Type.VIDEO -> CONTENT_TYPE_VIDEO } private fun getMetadataType(mediaType: MediaContent.Type) = when (mediaType) { MediaContent.Type.AUDIO -> MediaMetadata.MEDIA_TYPE_MUSIC_TRACK MediaContent.Type.VIDEO -> MediaMetadata.MEDIA_TYPE_MOVIE } } 

рдкреНрд░рдЬрдирди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ рдЖрджреЗрд╢ рджреЗрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рд╣рдореЗрдВ рдЕрдЧреНрд░рдгреА рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдкрд░ рдирд┐рд░реНрдгрдп рд▓реЗрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЙрдиреНрд╣реЗрдВ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреА рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рдХреЗ рдХреНрд░рдо рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рд░реЗрдбреАрдлреНрд▓реЛрд░рд┐рдВрдЧ () рд╡рд┐рдзрд┐ рдореЗрдВ рдЕрдкрдиреА рддрддреНрдкрд░рддрд╛ рдХреА рд╕реНрдерд┐рддрд┐ рджреЗ рд╕рдХрддрд╛ рд╣реИред рдкреВрд░рд╛ рдирдореВрдирд╛ рдХреЛрдб GitHub рдкрд░ рджреЗрдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред


рдХреНрдпрд╛ рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рдХреЗ рдмрд╛рдж рдЬреАрд╡рди рд╣реИ



рдЬрдм рдореИрдВрдиреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЗрдВ рдХреНрд░реЛрдордХрд╛рд╕реНрдЯ рд╕рдорд░реНрдерди рдХреЛ рдПрдХреАрдХреГрдд рдХрд┐рдпрд╛, рддреЛ рдореЗрд░реЗ рд▓рд┐рдП рдШрд░ рдкрд░ рдЖрдирд╛ рдФрд░ рд╣реЗрдбрдлреЛрди рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рди рдХреЗрд╡рд▓ Google рдкреБрд╕реНрддрдХреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ, рдмрд▓реНрдХрд┐ рдСрдбрд┐рдпреЛ рдкреБрд╕реНрддрдХреЛрдВ рдХрд╛ рдЖрдирдВрдж рд▓реЗрдирд╛ рдЕрдзрд┐рдХ рд╕реБрдЦрдж рд╣реЛ рдЧрдпрд╛ред рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЗ рд▓рд┐рдП, рд╡рд┐рднрд┐рдиреНрди рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдореЗрдВ рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣рд░ рдЬрдЧрд╣ рдЙрдЪрд┐рдд рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реА рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЗ рд▓рд┐рдП, рдпрд╣ рдКрдкрд░ рдЖрдпрд╛ред рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдпрд╣ рд▓реЗрдЦ рдЙрдкрдпреЛрдЧреА рдерд╛, рдФрд░ рдирд┐рдХрдЯ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдФрд░ рдЕрдзрд┐рдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╣реЛрдВрдЧреЗ рдЬреЛ рдбрд┐рдЬрд┐рдЯрд▓ рд╡рд╛рддрд╛рд╡рд░рдг рдХреЗ рд╕рд╛рде рдПрдХреАрдХреГрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ!

Source: https://habr.com/ru/post/hi442300/


All Articles