/* * Copyright (C) 2014 Haruki Hasegawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.h6ah4i.android.example.nativeopenslmediaplayer; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Instrumentation; import android.app.NativeActivity; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.PixelFormat; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.RelativeLayout; import android.widget.Toast; import java.io.File; import java.lang.reflect.Field; import java.util.Timer; import java.util.TimerTask; public class MainNativeActivity extends NativeActivity { // constants private static final int REQ_SONG_PICKER = 1; private long mNativeHandle; private RelativeLayout mControlsView; private Button mButtonOpenMedia; private Button mButtonPlay; private Button mButtonPause; private Button mButtonAbout; private Handler mHandler; private Runnable mDeferredSetupControlViews; private Runnable mDeferredHandleSongPicked; private Timer mTimer; private TimerTask mSendFakeTouchTask; static { System.loadLibrary("OpenSLMediaPlayer"); System.loadLibrary("OpenSLMediaPlayerNativeAPIExample"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mNativeHandle = getSuperNativeHandle(); mHandler = new Handler(); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); mDeferredSetupControlViews = new Runnable() { @Override public void run() { mDeferredSetupControlViews = null; setupControlViews(); } }; getWindow().getDecorView().post(mDeferredSetupControlViews); startFakeTouchTask(); } @Override protected void onPause() { cancelDeferredSetupControlViews(); cancelDeferredHandleSongPicked(); cancelSendFakeTouchTask(); removeControlViews(); super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQ_SONG_PICKER: if (resultCode == Activity.RESULT_OK) { final Uri uri = data.getData(); cancelDeferredHandleSongPicked(); mDeferredHandleSongPicked = new Runnable() { @Override public void run() { mDeferredHandleSongPicked = null; handleSongPicked(uri); } }; mHandler.post(mDeferredHandleSongPicked); } break; } } private void cancelDeferredSetupControlViews() { if (mDeferredSetupControlViews != null) { getWindow().getDecorView().removeCallbacks(mDeferredSetupControlViews); mDeferredSetupControlViews = null; } } private void cancelDeferredHandleSongPicked() { if (mDeferredHandleSongPicked != null) { mHandler.removeCallbacks(mDeferredHandleSongPicked); mDeferredHandleSongPicked = null; } } private void cancelSendFakeTouchTask() { if (mTimer != null) { mTimer.cancel(); mTimer.purge(); mTimer = null; mSendFakeTouchTask = null; } } private void startFakeTouchTask() { // Solution/Hack #3 - "Send fake touches!" // https://www.youtube.com/watch?v=F2ZDp-eNrh4 mTimer = new Timer(); mSendFakeTouchTask = new TimerTask() { @Override public void run() { try { Instrumentation instrumentation = new Instrumentation(); instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACKSLASH); } catch (SecurityException e) { // ignore SecurityException // "Injecting to another application requires INJECT_EVENTS permission" } } }; mTimer.schedule(mSendFakeTouchTask, 1000, 1000); } private void handleSongPicked(Uri uri) { final String filePath = resolveFilePathFromUri(this, uri); if (filePath != null) { nativeSetDataSourceAndPrepare(mNativeHandle, filePath); } else { Toast.makeText( this, "Sorry, this example does not support that URI", Toast.LENGTH_LONG).show(); } } private static String resolveFilePathFromUri(Context context, Uri uri) { String filePath = null; String scheme = uri.getScheme(); if ("file".equals(scheme)) { filePath = uri.getPath(); } else if ("content".equals(scheme)) { String path = uri.getPath(); if (new File(path).exists()) { filePath = path; } else { Cursor c = null; try { Uri mediaUri; if (path.startsWith("/document/audio:")) { final long id = Long.parseLong(path.substring("/document/audio:".length())); mediaUri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); } else { mediaUri = uri; } c = context.getContentResolver().query(mediaUri, new String[] { MediaStore.Audio.Media.DATA }, null, null, null); if (c.moveToFirst()) { filePath = c.getString(0); } } catch (RuntimeException e) { // just ignore errors... } finally { if (c != null) { c.close(); } } } } return filePath; } // Read NativeActivity.mNativeHandle field (= *ANativeActivity in C++) private long getSuperNativeHandle() { long handle = 0; try { Field f = NativeActivity.class.getDeclaredField("mNativeHandle"); f.setAccessible(true); handle = f.getLong(this); } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { } catch (IllegalArgumentException e) { } return handle; } @SuppressLint({ "InflateParams", "RtlHardcoded" }) private void setupControlViews() { // Create overlay window final WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); final LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); final Rect rectangle = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle); final int StatusBarHeight = rectangle.top; RelativeLayout controls = (RelativeLayout) inflater.inflate(R.layout.controls, null, true); WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); params.y = StatusBarHeight; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.gravity = Gravity.RIGHT | Gravity.TOP; wm.addView(controls, params); mControlsView = controls; mButtonOpenMedia = (Button) controls.findViewById(R.id.buttonOpenMedia); mButtonPlay = (Button) controls.findViewById(R.id.buttonPlay); mButtonPause = (Button) controls.findViewById(R.id.buttonPause); mButtonAbout = (Button) controls.findViewById(R.id.buttonAbout); mButtonOpenMedia.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MainNativeActivity.this.onOpenMediaButtonClicked(); } }); mButtonPlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MainNativeActivity.this.onPlayButtonClicked(); } }); mButtonPause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MainNativeActivity.this.onPauseButtonClicked(); } }); mButtonAbout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MainNativeActivity.this.onAboutButtonClicked(); } }); } private void removeControlViews() { if (mControlsView != null) { final WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); wm.removeView(mControlsView); mControlsView = null; } mButtonOpenMedia = null; mButtonPlay = null; mButtonPause = null; mButtonAbout = null; } private void launchSongPicker(int requestCode) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("audio/*"); Intent c = Intent.createChooser(intent, "Pick a music file"); startActivityForResult(c, requestCode); } protected void onOpenMediaButtonClicked() { launchSongPicker(REQ_SONG_PICKER); } protected void onPlayButtonClicked() { nativePlay(mNativeHandle); } protected void onPauseButtonClicked() { nativePause(mNativeHandle); } protected void onAboutButtonClicked() { Intent intent = new Intent(this, AboutActivity.class); startActivity(intent); } // // native methods // private static native int nativeSetDataSourceAndPrepare(long handle, String path); private static native int nativePlay(long handle); private static native int nativePause(long handle); }