Android 如何使用谷歌&x27;视频视图
我正在尝试在Android应用程序上使用谷歌的Android 如何使用谷歌&x27;视频视图,android,android-studio,video-streaming,google-vr-sdk,vrvideoview,Android,Android Studio,Video Streaming,Google Vr Sdk,Vrvideoview,我正在尝试在Android应用程序上使用谷歌的vrVideoView流式播放视频。是我当前尝试加载作为测试的视频。不过,我最终还是想从谷歌硬盘的URL加载一段视频。我目前正在使用教程中的代码。我不知道视频链接是否有问题,或者在将其传递到vrVideoView.loadVideo()之前是否需要对URI或URL进行处理 我已采取的步骤: 在AndroidManifest.xml中添加了以下行: <uses-permission android:name="android.permission
vrVideoView
流式播放视频。是我当前尝试加载作为测试的视频。不过,我最终还是想从谷歌硬盘的URL加载一段视频。我目前正在使用教程中的代码。我不知道视频链接是否有问题,或者在将其传递到vrVideoView.loadVideo()
之前是否需要对URI或URL进行处理
我已采取的步骤:
在AndroidManifest.xml中添加了以下行:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
我的完整MainActivity.java文件是:
package com.example.kurtc.vr_test_2;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;
import com.google.vr.sdk.widgets.video.VrVideoEventListener;
import com.google.vr.sdk.widgets.video.VrVideoView;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
/**
* Preserve the video's state when rotating the phone.
*/
private static final String STATE_IS_PAUSED = "isPaused";
private static final String STATE_PROGRESS_TIME = "progressTime";
/**
* The video duration doesn't need to be preserved, but it is saved in this example. This allows
* the seekBar to be configured during {@link #onRestoreInstanceState(Bundle)} rather than waiting
* for the video to be reloaded and analyzed. This avoid UI jank.
*/
private static final String STATE_VIDEO_DURATION = "videoDuration";
private VideoLoaderTask backgroundVideoLoaderTask;
/**
* The video view and its custom UI elements.
*/
protected VrVideoView videoWidgetView;
/**
* Seeking UI & progress indicator. The seekBar's progress value represents milliseconds in the
* video.
*/
private SeekBar seekBar;
private ImageButton volumeToggle;
private ImageButton playToggle;
protected ImageView playIcon;
private boolean isMuted;
/**
* By default, the video will start playing as soon as it is loaded. This can be changed by using
* {@link VrVideoView#pauseVideo()} after loading the video.
*/
private boolean isPaused = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBar = (SeekBar) findViewById(R.id.seek_bar);
seekBar.setOnSeekBarChangeListener(new SeekBarListener());
videoWidgetView = (VrVideoView) findViewById(R.id.video_view);
videoWidgetView.setEventListener(new ActivityEventListener());
volumeToggle = (ImageButton) findViewById(R.id.volume_toggle);
volumeToggle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
setIsMuted(!isMuted);
}
});
playToggle = (ImageButton) findViewById((R.id.play_toggle));
playToggle.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
handlePause();
}
});
playIcon = (ImageView) findViewById(R.id.playButton);
playIcon.setVisibility(View.INVISIBLE);
playIcon.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
handlePause();
}
});
// Load the bitmap in a background thread to avoid blocking the UI thread. This operation can
// take 100s of milliseconds.
if (backgroundVideoLoaderTask != null) {
// Cancel any task from a previous intent sent to this activity.
backgroundVideoLoaderTask.cancel(true);
}
backgroundVideoLoaderTask = new VideoLoaderTask();
backgroundVideoLoaderTask.execute("https://youtu.be/DlcBadfFUC0");
}
//playbutton replaced: volume_off, volume_on
private void setIsMuted(boolean isMuted) {
this.isMuted = isMuted;
volumeToggle.setImageResource(isMuted ? R.drawable.playbutton : R.drawable.playbutton);
videoWidgetView.setVolume(isMuted ? 0.0f : 1.0f);
}
private void handlePause() {
togglePause();
updatePauseButtons();
}
//playbutton replaced: play_off, play_on
private void updatePauseButtons(){
playToggle.setImageResource(isPaused ? R.drawable.playbutton : R.drawable.playbutton);
playIcon.setVisibility(isPaused ? View.VISIBLE: View.INVISIBLE);
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putLong(STATE_PROGRESS_TIME, videoWidgetView.getCurrentPosition());
savedInstanceState.putLong(STATE_VIDEO_DURATION, videoWidgetView.getDuration());
savedInstanceState.putBoolean(STATE_IS_PAUSED, isPaused);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
long progressTime = savedInstanceState.getLong(STATE_PROGRESS_TIME);
videoWidgetView.seekTo(progressTime);
seekBar.setMax((int) savedInstanceState.getLong(STATE_VIDEO_DURATION));
seekBar.setProgress((int) progressTime);
isPaused = savedInstanceState.getBoolean(STATE_IS_PAUSED);
if (isPaused) {
videoWidgetView.pauseVideo();
}
}
@Override
protected void onPause() {
super.onPause();
// Prevent the view from rendering continuously when in the background.
videoWidgetView.pauseRendering();
// If the video is playing when onPause() is called, the default behavior will be to pause
// the video and keep it paused when onResume() is called.
isPaused = true;
updatePauseButtons();
}
@Override
protected void onResume() {
super.onResume();
// Resume the 3D rendering.
videoWidgetView.resumeRendering();
}
@Override
protected void onDestroy() {
// Destroy the widget and free memory.
videoWidgetView.shutdown();
super.onDestroy();
}
private void togglePause() {
if (isPaused) {
videoWidgetView.playVideo();
} else {
videoWidgetView.pauseVideo();
}
isPaused = !isPaused;
}
/**
* When the user manipulates the seek bar, update the video position.
*/
private class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
videoWidgetView.seekTo(progress);
} // else this was from the ActivityEventHandler.onNewFrame()'s seekBar.setProgress update.
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
}
/**
* Listen to the important events from widget.
*/
private class ActivityEventListener extends VrVideoEventListener {
/**
* Called by video widget on the UI thread when it's done loading the video.
*/
@Override
public void onLoadSuccess() {
seekBar.setMax((int) videoWidgetView.getDuration());
}
/**
* Called by video widget on the UI thread on any asynchronous error.
*/
@Override
public void onLoadError(String errorMessage) {
// An error here is normally due to being unable to decode the video format.
Toast.makeText(
MainActivity.this, "Error loading video: " + errorMessage, Toast.LENGTH_LONG)
.show();
}
@Override
public void onClick() {
}
/**
* Update the UI every frame.
*/
@Override
public void onNewFrame() {
seekBar.setProgress((int) videoWidgetView.getCurrentPosition());
}
/**
* Make the video play in a loop. This method could also be used to move to the next video in
* a playlist.
*/
@Override
public void onCompletion() {
videoWidgetView.seekTo(0);
}
}
/**
* Helper class to manage threading.
*/
class VideoLoaderTask extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... fileInformation) {
try {
VrVideoView.Options options = new VrVideoView.Options();
options.inputType = VrVideoView.Options.TYPE_MONO;
options.inputFormat = VrVideoView.Options.FORMAT_HLS;
videoWidgetView.loadVideo(Uri.parse("https://youtu.be/DlcBadfFUC0"), options);
}
catch (IOException e) {
// An error here is normally due to being unable to locate the file.
// Since this is a background thread, we need to switch to the main thread to show a toast.
videoWidgetView.post(new Runnable() {
@Override
public void run() {
Toast
.makeText(MainActivity.this, "Error opening file. ", Toast.LENGTH_LONG)
.show();
}
});
}
return true;
}
}
}
package com.example.kurtc.vr_test_2;
导入android.net.Uri;
导入android.os.AsyncTask;
导入android.os.Bundle;
导入android.support.v7.app.AppActivity;
导入android.view.view;
导入android.widget.ImageButton;
导入android.widget.ImageView;
导入android.widget.SeekBar;
导入android.widget.Toast;
导入com.google.vr.sdk.widgets.video.VrVideoEventListener;
导入com.google.vr.sdk.widgets.video.VrVideoView;
导入java.io.IOException;
公共类MainActivity扩展了AppCompatActivity{
/**
*旋转手机时保留视频的状态。
*/
私有静态最终字符串状态\u为\u PAUSED=“isPaused”;
私有静态最终字符串状态\u PROGRESS\u TIME=“progressTime”;
/**
*视频持续时间不需要保留,但在本例中已保存。这允许
*seekBar将在{@link#onRestoreInstanceState(Bundle)}期间配置,而不是等待
*用于重新加载和分析视频。这避免了UI jank。
*/
私有静态最终字符串状态\u VIDEO\u DURATION=“videoDuration”;
私人视频加载任务背景视频加载任务;
/**
*视频视图及其自定义UI元素。
*/
受保护的VrVideoView videoWidgetView;
/**
*正在查找UI和进度指示器。seekBar的进度值表示
*录像带。
*/
私人SeekBar SeekBar;
专用图像按钮音量切换;
私有图像按钮播放切换;
受保护的ImageView播放图标;
私有布尔运算;
/**
*默认情况下,视频一加载就开始播放。可以使用
*加载视频后{@link VrVideoView#pauseVideo()}。
*/
私有布尔值isPaused=false;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBar=(seekBar)findViewById(R.id.seek\u bar);
setOnSeekBarListener(新的SeekBarListener());
videoWidgetView=(VrVideoView)findViewById(R.id.video\u视图);
videoWidgetView.setEventListener(新的ActivityEventListener());
volumeToggle=(ImageButton)findViewById(R.id.volume\u切换);
volumeToggle.setOnClickListener(新视图.OnClickListener(){
公共void onClick(视图v){
setIsMuted(!isMuted);
}
});
playToggle=(ImageButton)findViewById((R.id.play_toggle));
playToggle.setOnClickListener(新视图.OnClickListener(){
公共void onClick(视图v){
handlePause();
}
});
playIcon=(ImageView)findViewById(R.id.playButton);
playIcon.setVisibility(视图.不可见);
playIcon.setOnClickListener(新视图.OnClickListener(){
公共void onClick(视图v){
handlePause();
}
});
//在后台线程中加载位图以避免阻塞UI线程。此操作可以
//需要100毫秒。
if(backgroundVideoLoaderTask!=null){
//取消以前发送到此活动的意图中的任何任务。
backgroundVideoLoaderTask.cancel(真);
}
backgroundVideoLoaderTask=新的VideoLoaderTask();
backgroundVideoLoaderTask.execute(“https://youtu.be/DlcBadfFUC0");
}
//更换播放按钮:音量关闭,音量打开
私有void setIsMuted(布尔值isMuted){
this.ismute=ismute;
volumeToggle.setImageResource(ismute?R.drawable.playbutton:R.drawable.playbutton);
videoWidgetView.setVolume(isMuted?0.0f:1.0f);
}
私有无效handlePause(){
切换暂停();
updatePauseButtons();
}
//更换播放按钮:播放关闭,播放打开
私有void updatePauseButtons(){
playToggle.setImageResource(isPaused?R.drawable.playbutton:R.drawable.playbutton);
playIcon.setVisibility(isPaused?View.VISIBLE:View.INVISIBLE);
}
@凌驾
SaveInstanceState上的公共无效(Bundle savedInstanceState){
savedInstanceState.putLong(状态进度时间,videoWidgetView.getCurrentPosition());
savedInstanceState.putLong(STATE_VIDEO_DURATION,videoWidgetView.getDuration());
savedInstanceState.putBoolean(状态已暂停,已暂停);
super.onSaveInstanceState(savedInstanceState);
}
@凌驾
RestoreInstanceState上的公共无效(Bundle savedInstanceState){
super.onRestoreInstanceState(savedInstanceState);
long progressTime=savedInstanceState.getLong(状态进度时间);
videoWidgetView.seekTo(progressTime);
seekBar.setMax((int)savedInstanceState.getLong(STATE_VIDEO_DURATION));
seekBar.setProgress((int)progressTime);
isPaused=savedInstanceState.getBoolean(状态为暂停);
如果(i暂停){
videoWidgetView.pauseVideo();
}
}
@凌驾
受保护的void onPause(){
super.onPause();
//防止在背景中连续渲染视图。
videoWidgetView.pauseRendering();
//如果调用onPause()时正在播放视频,则默认行为为暂停
//调用onResume()时,播放视频并使其暂停。
isPaused=真;
updatePauseButtons();
}
@凌驾
受保护的void onResume(){
super.onResume();
//恢复三维渲染。
videoWidgetView.resumeRendering();
}
@凌驾
受保护的空onDestroy(){
//销毁小部件并释放内存。
videoWidgetView.shutdown();
super.ondestory();
}
私有void togglePause(){
如果(i暂停){
videoWidgetView.playVideo();
}否则{
videoWidgetView.pauseVideo();
}
伊斯帕斯
package com.example.kurtc.vr_test_2;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;
import com.google.vr.sdk.widgets.video.VrVideoEventListener;
import com.google.vr.sdk.widgets.video.VrVideoView;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
/**
* Preserve the video's state when rotating the phone.
*/
private static final String STATE_IS_PAUSED = "isPaused";
private static final String STATE_PROGRESS_TIME = "progressTime";
/**
* The video duration doesn't need to be preserved, but it is saved in this example. This allows
* the seekBar to be configured during {@link #onRestoreInstanceState(Bundle)} rather than waiting
* for the video to be reloaded and analyzed. This avoid UI jank.
*/
private static final String STATE_VIDEO_DURATION = "videoDuration";
private VideoLoaderTask backgroundVideoLoaderTask;
/**
* The video view and its custom UI elements.
*/
protected VrVideoView videoWidgetView;
/**
* Seeking UI & progress indicator. The seekBar's progress value represents milliseconds in the
* video.
*/
private SeekBar seekBar;
private ImageButton volumeToggle;
private ImageButton playToggle;
protected ImageView playIcon;
private boolean isMuted;
/**
* By default, the video will start playing as soon as it is loaded. This can be changed by using
* {@link VrVideoView#pauseVideo()} after loading the video.
*/
private boolean isPaused = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekBar = (SeekBar) findViewById(R.id.seek_bar);
seekBar.setOnSeekBarChangeListener(new SeekBarListener());
videoWidgetView = (VrVideoView) findViewById(R.id.video_view);
videoWidgetView.setEventListener(new ActivityEventListener());
volumeToggle = (ImageButton) findViewById(R.id.volume_toggle);
volumeToggle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
setIsMuted(!isMuted);
}
});
playToggle = (ImageButton) findViewById((R.id.play_toggle));
playToggle.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
handlePause();
}
});
playIcon = (ImageView) findViewById(R.id.playButton);
playIcon.setVisibility(View.INVISIBLE);
playIcon.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
handlePause();
}
});
// Load the bitmap in a background thread to avoid blocking the UI thread. This operation can
// take 100s of milliseconds.
if (backgroundVideoLoaderTask != null) {
// Cancel any task from a previous intent sent to this activity.
backgroundVideoLoaderTask.cancel(true);
}
backgroundVideoLoaderTask = new VideoLoaderTask();
backgroundVideoLoaderTask.execute("https://youtu.be/DlcBadfFUC0");
}
//playbutton replaced: volume_off, volume_on
private void setIsMuted(boolean isMuted) {
this.isMuted = isMuted;
volumeToggle.setImageResource(isMuted ? R.drawable.playbutton : R.drawable.playbutton);
videoWidgetView.setVolume(isMuted ? 0.0f : 1.0f);
}
private void handlePause() {
togglePause();
updatePauseButtons();
}
//playbutton replaced: play_off, play_on
private void updatePauseButtons(){
playToggle.setImageResource(isPaused ? R.drawable.playbutton : R.drawable.playbutton);
playIcon.setVisibility(isPaused ? View.VISIBLE: View.INVISIBLE);
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putLong(STATE_PROGRESS_TIME, videoWidgetView.getCurrentPosition());
savedInstanceState.putLong(STATE_VIDEO_DURATION, videoWidgetView.getDuration());
savedInstanceState.putBoolean(STATE_IS_PAUSED, isPaused);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
long progressTime = savedInstanceState.getLong(STATE_PROGRESS_TIME);
videoWidgetView.seekTo(progressTime);
seekBar.setMax((int) savedInstanceState.getLong(STATE_VIDEO_DURATION));
seekBar.setProgress((int) progressTime);
isPaused = savedInstanceState.getBoolean(STATE_IS_PAUSED);
if (isPaused) {
videoWidgetView.pauseVideo();
}
}
@Override
protected void onPause() {
super.onPause();
// Prevent the view from rendering continuously when in the background.
videoWidgetView.pauseRendering();
// If the video is playing when onPause() is called, the default behavior will be to pause
// the video and keep it paused when onResume() is called.
isPaused = true;
updatePauseButtons();
}
@Override
protected void onResume() {
super.onResume();
// Resume the 3D rendering.
videoWidgetView.resumeRendering();
}
@Override
protected void onDestroy() {
// Destroy the widget and free memory.
videoWidgetView.shutdown();
super.onDestroy();
}
private void togglePause() {
if (isPaused) {
videoWidgetView.playVideo();
} else {
videoWidgetView.pauseVideo();
}
isPaused = !isPaused;
}
/**
* When the user manipulates the seek bar, update the video position.
*/
private class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
videoWidgetView.seekTo(progress);
} // else this was from the ActivityEventHandler.onNewFrame()'s seekBar.setProgress update.
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
}
/**
* Listen to the important events from widget.
*/
private class ActivityEventListener extends VrVideoEventListener {
/**
* Called by video widget on the UI thread when it's done loading the video.
*/
@Override
public void onLoadSuccess() {
seekBar.setMax((int) videoWidgetView.getDuration());
}
/**
* Called by video widget on the UI thread on any asynchronous error.
*/
@Override
public void onLoadError(String errorMessage) {
// An error here is normally due to being unable to decode the video format.
Toast.makeText(
MainActivity.this, "Error loading video: " + errorMessage, Toast.LENGTH_LONG)
.show();
}
@Override
public void onClick() {
}
/**
* Update the UI every frame.
*/
@Override
public void onNewFrame() {
seekBar.setProgress((int) videoWidgetView.getCurrentPosition());
}
/**
* Make the video play in a loop. This method could also be used to move to the next video in
* a playlist.
*/
@Override
public void onCompletion() {
videoWidgetView.seekTo(0);
}
}
/**
* Helper class to manage threading.
*/
class VideoLoaderTask extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... fileInformation) {
try {
VrVideoView.Options options = new VrVideoView.Options();
options.inputType = VrVideoView.Options.TYPE_MONO;
options.inputFormat = VrVideoView.Options.FORMAT_HLS;
videoWidgetView.loadVideo(Uri.parse("https://youtu.be/DlcBadfFUC0"), options);
}
catch (IOException e) {
// An error here is normally due to being unable to locate the file.
// Since this is a background thread, we need to switch to the main thread to show a toast.
videoWidgetView.post(new Runnable() {
@Override
public void run() {
Toast
.makeText(MainActivity.this, "Error opening file. ", Toast.LENGTH_LONG)
.show();
}
});
}
return true;
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_layout"
android:padding="10dip"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
<com.google.vr.sdk.widgets.video.VrVideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:scrollbars="none"
android:layout_height="250dip"/>
<ImageView
android:id = "@+id/playButton"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_marginBottom="10dip"
android:background="@drawable/playbutton"
android:adjustViewBounds="true"
android:scaleType="fitXY" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageButton
android:background="@android:color/transparent"
android:id="@+id/play_toggle"
android:paddingTop="4dp"
android:paddingStart="0dp"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:src="@drawable/playbutton" />
<!-- Seeking UI & progress indicator. play_on volume_on -->
<SeekBar
android:id="@+id/seek_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_height="32dp"
android:layout_weight="8"
android:layout_width="0dp"/>
<ImageButton
android:background="@android:color/transparent"
android:id="@+id/volume_toggle"
android:paddingTop="4dp"
android:paddingStart="0dp"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:src="@drawable/playbutton"/>
</LinearLayout>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kurtc.vr_test_2">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>