diff --git a/app/build.gradle b/app/build.gradle index 97a4bcd6..b0c072ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion '25.0.0' + compileSdkVersion 25 + buildToolsVersion '25.0.2' defaultConfig { applicationId "com.asha.md360player4android" @@ -23,9 +23,11 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.2.0' + compile 'com.android.support:appcompat-v7:25.3.1' compile 'com.squareup.picasso:picasso:2.5.2' //required, enough for most devices. + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' compile 'tv.danmaku.ijk.media:ijkplayer-java:0.6.0' compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.6.0' compile project(path: ':vrlib') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 64e02076..4c72f50d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,6 +21,8 @@ + + diff --git a/app/src/main/java/com/asha/md360player4android/CubemapPlayerActivity.java b/app/src/main/java/com/asha/md360player4android/CubemapPlayerActivity.java new file mode 100644 index 00000000..f092f8d9 --- /dev/null +++ b/app/src/main/java/com/asha/md360player4android/CubemapPlayerActivity.java @@ -0,0 +1,152 @@ +package com.asha.md360player4android; + +import android.content.ContentResolver; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.DrawableRes; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.asha.vrlib.MDVRLibrary; +import com.asha.vrlib.model.MDRay; +import com.asha.vrlib.plugins.hotspot.IMDHotspot; +import com.asha.vrlib.texture.MD360BitmapTexture; +import com.asha.vrlib.texture.MD360CubemapTexture; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import static com.squareup.picasso.MemoryPolicy.NO_CACHE; +import static com.squareup.picasso.MemoryPolicy.NO_STORE; + +/** + * Created by hzqiujiadi on 16/4/5. + * hzqiujiadi ashqalcn@gmail.com + */ +public class CubemapPlayerActivity extends MD360PlayerActivity { + + private static final String TAG = "BitmapPlayerActivity"; + + private Uri nextUri = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + findViewById(R.id.control_next).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + busy(); + //nextUri = getDrawableUri(R.drawable.texture); + getVRLibrary().notifyPlayerChanged(); + } + }); + } + + private Target mTarget;// keep the reference for picasso. + + private void loadImage(Uri uri, final MD360CubemapTexture.Callback callback){ + mTarget = new Target() { + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + Log.d(TAG, "loaded image, size:" + bitmap.getWidth() + "," + bitmap.getHeight()); + + // notify if size changed + getVRLibrary().onTextureResize(bitmap.getWidth(), bitmap.getHeight()); + + // texture + callback.texture(bitmap); + //cancelBusy(); + } + + @Override + public void onBitmapFailed(Drawable errorDrawable) { + + } + + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + + } + }; + + Log.d(TAG, "load image with max texture size:" + callback.getMaxTextureSize()); + Picasso.with(getApplicationContext()) + .load(uri) + .resize(callback.getMaxTextureSize(),callback.getMaxTextureSize()) + .onlyScaleDown() + .centerInside() + .memoryPolicy(NO_CACHE, NO_STORE) + .into(mTarget); + } + + private Uri currentUri(){ + if (nextUri == null){ + return getUri(); + } else { + return nextUri; + } + } + + @Override + protected MDVRLibrary createVRLibrary() { + return MDVRLibrary.with(this) + .displayMode(MDVRLibrary.DISPLAY_MODE_NORMAL) + .interactiveMode(MDVRLibrary.INTERACTIVE_MODE_TOUCH) + .projectionMode(MDVRLibrary.PROJECTION_MODE_CUBE) // needed + .asCubemap(new MDVRLibrary.ICubemapProvider() { + @Override + public void onProvideCubemap(MD360CubemapTexture.Callback callback, int cubeFace) { + Log.d(TAG, "Load face: " + cubeFace); + + switch(cubeFace) { + case MD360CubemapTexture.CUBE_FRONT: + nextUri = getDrawableUri(R.drawable.cube_front); + break; + case MD360CubemapTexture.CUBE_BACK: + nextUri = getDrawableUri(R.drawable.cube_back); + break; + case MD360CubemapTexture.CUBE_TOP: + nextUri = getDrawableUri(R.drawable.cube_top); + break; + case MD360CubemapTexture.CUBE_BOTTOM: + nextUri = getDrawableUri(R.drawable.cube_bottom); + break; + case MD360CubemapTexture.CUBE_LEFT: + nextUri = getDrawableUri(R.drawable.cube_left); + break; + case MD360CubemapTexture.CUBE_RIGHT: + nextUri = getDrawableUri(R.drawable.cube_right); + break; + default: + return; + } + + loadImage(currentUri(), callback); + } + + @Override + public void onReady() { + // This can be used to hide a loading view and show the library view + Toast.makeText(CubemapPlayerActivity.this, "CubeMap Ready", Toast.LENGTH_SHORT).show(); + cancelBusy(); + } + }) + .listenTouchPick(new MDVRLibrary.ITouchPickListener() { + @Override + public void onHotspotHit(IMDHotspot hitHotspot, MDRay ray) { + Log.d(TAG,"Ray:" + ray + ", hitHotspot:" + hitHotspot); + } + }) + .pinchEnabled(true) + .build(findViewById(R.id.gl_view)); + } + + private Uri getDrawableUri(@DrawableRes int resId){ + Resources resources = getResources(); + return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + resources.getResourcePackageName(resId) + '/' + resources.getResourceTypeName(resId) + '/' + resources.getResourceEntryName(resId) ); + } +} diff --git a/app/src/main/java/com/asha/md360player4android/DemoActivity.java b/app/src/main/java/com/asha/md360player4android/DemoActivity.java index ce256021..f86af0f0 100644 --- a/app/src/main/java/com/asha/md360player4android/DemoActivity.java +++ b/app/src/main/java/com/asha/md360player4android/DemoActivity.java @@ -105,6 +105,22 @@ public void onClick(View v) { } } }); + + findViewById(R.id.cubemap_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String url = et.getText().toString(); + + MD360PlayerActivity.startCubemap(DemoActivity.this, null); + } + }); + + findViewById(R.id.recycler_view_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + RecyclerViewActivity.start(DemoActivity.this); + } + }); } private Uri getDrawableUri(@DrawableRes int resId){ diff --git a/app/src/main/java/com/asha/md360player4android/MD360PlayerActivity.java b/app/src/main/java/com/asha/md360player4android/MD360PlayerActivity.java index d708f8d0..1c2549d6 100644 --- a/app/src/main/java/com/asha/md360player4android/MD360PlayerActivity.java +++ b/app/src/main/java/com/asha/md360player4android/MD360PlayerActivity.java @@ -21,6 +21,7 @@ import com.asha.vrlib.MDDirectorCamUpdate; import com.asha.vrlib.MDVRLibrary; +import com.asha.vrlib.model.MDHitEvent; import com.asha.vrlib.model.MDHotspotBuilder; import com.asha.vrlib.model.MDPosition; import com.asha.vrlib.model.MDRay; @@ -105,6 +106,10 @@ public static void startBitmap(Context context, Uri uri){ start(context, uri, BitmapPlayerActivity.class); } + public static void startCubemap(Context context, Uri uri){ + start(context, uri, CubemapPlayerActivity.class); + } + private static void start(Context context, Uri uri, Class clz){ Intent i = new Intent(context,clz); i.setData(uri); @@ -363,9 +368,11 @@ public void onClick(View v) { final TextView hotspotText = (TextView) findViewById(R.id.hotspot_text); final TextView directorBriefText = (TextView) findViewById(R.id.director_brief_text); - getVRLibrary().setEyePickChangedListener(new MDVRLibrary.IEyePickListener() { + getVRLibrary().setEyePickChangedListener(new MDVRLibrary.IEyePickListener2() { @Override - public void onHotspotHit(IMDHotspot hotspot, long hitTimestamp) { + public void onHotspotHit(MDHitEvent hitEvent) { + IMDHotspot hotspot = hitEvent.getHotspot(); + long hitTimestamp = hitEvent.getTimestamp(); String text = hotspot == null ? "nop" : String.format(Locale.CHINESE, "%s %fs", hotspot.getTitle(), (System.currentTimeMillis() - hitTimestamp) / 1000.0f ); hotspotText.setText(text); diff --git a/app/src/main/java/com/asha/md360player4android/RecyclerViewActivity.java b/app/src/main/java/com/asha/md360player4android/RecyclerViewActivity.java new file mode 100644 index 00000000..d9558621 --- /dev/null +++ b/app/src/main/java/com/asha/md360player4android/RecyclerViewActivity.java @@ -0,0 +1,235 @@ +package com.asha.md360player4android; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.DrawableRes; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.asha.vrlib.MDVRLibrary; +import com.asha.vrlib.model.MDHitEvent; +import com.asha.vrlib.texture.MD360BitmapTexture; +import com.google.android.apps.muzei.render.GLTextureView; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import java.util.ArrayList; +import java.util.List; + +public class RecyclerViewActivity extends AppCompatActivity { + + private static final String TAG = "MainActivity"; + + private Uri[] sMockData; + + private VRLibManager manager; + + public static void start(Context context) { + Intent i = new Intent(context, RecyclerViewActivity.class); + context.startActivity(i); + } + + public RecyclerViewActivity() {} + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + sMockData = new Uri[] { + getDrawableUri(R.drawable.bitmap360) + ,getDrawableUri(R.drawable.texture) + }; + + setContentView(R.layout.activity_main); + manager = new VRLibManager(this); + + RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); + final FeedAdapter adapter = new FeedAdapter(); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setItemAnimator(null); + recyclerView.setAdapter(adapter); + } + + @Override + protected void onResume() { + super.onResume(); + manager.fireResumed(); + } + + @Override + protected void onPause() { + super.onPause(); + manager.firePaused(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + manager.fireDestroy(); + } + + private static class FeedModel { + + private final Uri uri; + private final int type; + + public FeedModel(int type, Uri uri) { + this.type = type; + this.uri = uri; + } + } + + private abstract class FeedVH extends RecyclerView.ViewHolder { + + public FeedVH(ViewGroup vp, int layoutId) { + super(create(vp, layoutId)); + } + + public abstract void bind(FeedModel feedModel); + } + + private class FeedTextVH extends FeedVH { + + public FeedTextVH(ViewGroup vp) { + super(vp, R.layout.feed_text_layout); + } + + @Override + public void bind(FeedModel feedModel) { + } + } + + private class FeedVRVH extends FeedVH implements MDVRLibrary.IBitmapProvider { + + private TextView text; + + private GLTextureView glTextureView; + + private ViewGroup parent; + + private MDVRLibrary vrlib; + + private FeedModel model; + + private long ts; + + public FeedVRVH(ViewGroup vp) { + super(vp, R.layout.feed_panorama_layout); + text = (TextView) itemView.findViewById(R.id.feed_text); + glTextureView = (GLTextureView) itemView.findViewById(R.id.feed_texture_view); + parent = (ViewGroup) glTextureView.getParent(); + } + + @Override + public void bind(FeedModel model) { + this.model = model; + ensureVRLib(); + vrlib.notifyPlayerChanged(); + } + + private void ensureVRLib() { + if (vrlib == null) { + vrlib = manager.create(this, glTextureView); + vrlib.setEyePickChangedListener(new MDVRLibrary.IEyePickListener2() { + @Override + public void onHotspotHit(MDHitEvent hitEvent) { + long delta = System.currentTimeMillis() - ts; + if (delta < 500) { + return; + } + + String brief = vrlib.getDirectorBrief().toString(); + text.setText(brief); + ts = System.currentTimeMillis(); + } + }); + } + } + + @Override + public void onProvideBitmap(final MD360BitmapTexture.Callback callback) { + if (model == null) { + return; + } + + Picasso.with(itemView.getContext()).load(model.uri).into(new Target() { + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + vrlib.onTextureResize(bitmap.getWidth(), bitmap.getHeight()); + callback.texture(bitmap); + } + + @Override + public void onBitmapFailed(Drawable errorDrawable) { + + } + + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + + } + }); + } + } + + private Uri getDrawableUri(@DrawableRes int resId){ + Resources resources = getResources(); + return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + resources.getResourcePackageName(resId) + '/' + resources.getResourceTypeName(resId) + '/' + resources.getResourceEntryName(resId) ); + } + + private class FeedAdapter extends RecyclerView.Adapter { + + private List feeds = new ArrayList<>(); + + public FeedAdapter() { + int i = 0; + while (i++ < 50) { + Uri uri = sMockData[(int) (Math.random() * sMockData.length)]; + feeds.add(new FeedModel(Math.random() > 0.3 ? 0 : 1, uri)); + } + } + + @Override + public FeedVH onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == 0) { + return new FeedVRVH(parent); + } else { + return new FeedTextVH(parent); + } + } + + @Override + public void onBindViewHolder(FeedVH holder, int position) { + holder.bind(feeds.get(position)); + } + + @Override + public void onViewRecycled(FeedVH holder) { + super.onViewRecycled(holder); + } + + @Override + public int getItemViewType(int position) { + return feeds.get(position).type; + } + + @Override + public int getItemCount() { + return feeds.size(); + } + } + + private static View create(ViewGroup vp, int layout) { + return LayoutInflater.from(vp.getContext()).inflate(layout, vp, false); + } +} diff --git a/app/src/main/java/com/asha/md360player4android/VRLibManager.java b/app/src/main/java/com/asha/md360player4android/VRLibManager.java new file mode 100644 index 00000000..d9212a7d --- /dev/null +++ b/app/src/main/java/com/asha/md360player4android/VRLibManager.java @@ -0,0 +1,68 @@ +package com.asha.md360player4android; + +import android.content.Context; + +import com.asha.vrlib.MDVRLibrary; +import com.asha.vrlib.model.MDPinchConfig; +import com.google.android.apps.muzei.render.GLTextureView; + +import java.util.LinkedList; +import java.util.List; + +import static com.asha.vrlib.MDVRLibrary.INTERACTIVE_MODE_CARDBORAD_MOTION_WITH_TOUCH; + +/** + * Created by hzqiujiadi on 2017/9/7. + * hzqiujiadi ashqalcn@gmail.com + */ + +public class VRLibManager { + private Context context; + + private boolean isResumed; + + private List libs = new LinkedList<>(); + + public VRLibManager(Context context) { + this.context = context; + } + + public MDVRLibrary create(MDVRLibrary.IBitmapProvider provider, GLTextureView textureView) { + MDVRLibrary lib = MDVRLibrary.with(context) + .asBitmap(provider) + .pinchConfig(new MDPinchConfig().setMin(0.8f).setSensitivity(1).setDefaultValue(0.8f)) + .touchSensitivity(2) + .interactiveMode(INTERACTIVE_MODE_CARDBORAD_MOTION_WITH_TOUCH) + .build(textureView); + add(lib); + return lib; + } + + private void add(MDVRLibrary lib) { + if (isResumed) { + lib.onResume(context); + } + + libs.add(lib); + } + + public void fireResumed() { + isResumed = true; + for (MDVRLibrary library : libs) { + library.onResume(context); + } + } + + public void firePaused() { + isResumed = false; + for (MDVRLibrary library : libs) { + library.onPause(context); + } + } + + public void fireDestroy() { + for (MDVRLibrary library : libs) { + library.onDestroy(); + } + } +} diff --git a/app/src/main/res/drawable-xhdpi/cube_back.jpg b/app/src/main/res/drawable-xhdpi/cube_back.jpg new file mode 100644 index 00000000..4e17b779 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_back.jpg differ diff --git a/app/src/main/res/drawable-xhdpi/cube_bottom.jpg b/app/src/main/res/drawable-xhdpi/cube_bottom.jpg new file mode 100644 index 00000000..893f3947 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_bottom.jpg differ diff --git a/app/src/main/res/drawable-xhdpi/cube_front.jpg b/app/src/main/res/drawable-xhdpi/cube_front.jpg new file mode 100644 index 00000000..470a6797 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_front.jpg differ diff --git a/app/src/main/res/drawable-xhdpi/cube_left.jpg b/app/src/main/res/drawable-xhdpi/cube_left.jpg new file mode 100644 index 00000000..5750b91a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_left.jpg differ diff --git a/app/src/main/res/drawable-xhdpi/cube_right.jpg b/app/src/main/res/drawable-xhdpi/cube_right.jpg new file mode 100644 index 00000000..89630371 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_right.jpg differ diff --git a/app/src/main/res/drawable-xhdpi/cube_top.jpg b/app/src/main/res/drawable-xhdpi/cube_top.jpg new file mode 100644 index 00000000..4db3c2a3 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/cube_top.jpg differ diff --git a/app/src/main/res/layout/activity_demo.xml b/app/src/main/res/layout/activity_demo.xml index 83d2b450..a8b366db 100644 --- a/app/src/main/res/layout/activity_demo.xml +++ b/app/src/main/res/layout/activity_demo.xml @@ -37,6 +37,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> +