অ্যান্ড্রয়েড সংস্করণ জুড়ে কন্ট্রোলার সমর্থন করে

আপনি যদি আপনার গেমে গেম কন্ট্রোলারদের সমর্থন করেন, তাহলে Android এর বিভিন্ন সংস্করণে চলমান ডিভাইস জুড়ে আপনার গেমটি ধারাবাহিকভাবে কন্ট্রোলারদের প্রতি সাড়া দেয় তা নিশ্চিত করা আপনার দায়িত্ব। এটি আপনার গেমটিকে আরও বৃহত্তর দর্শকদের কাছে পৌঁছাতে দেয় এবং আপনার খেলোয়াড়রা তাদের অ্যান্ড্রয়েড ডিভাইসগুলি স্যুইচ বা আপগ্রেড করার পরেও তাদের কন্ট্রোলারের সাথে একটি নিরবচ্ছিন্ন গেমপ্লে অভিজ্ঞতা উপভোগ করতে পারে৷

এই পাঠটি দেখায় যে কীভাবে Android 4.1 এবং উচ্চতর সংস্করণে উপলব্ধ APIগুলিকে একটি পশ্চাদগামী সামঞ্জস্যপূর্ণ উপায়ে ব্যবহার করতে হয়, আপনার গেমটিকে Android 3.1 এবং উচ্চতর সংস্করণে চলমান ডিভাইসগুলিতে নিম্নলিখিত বৈশিষ্ট্যগুলিকে সমর্থন করতে সক্ষম করে:

  • একটি নতুন গেম কন্ট্রোলার যোগ করা, পরিবর্তন করা বা সরানো হয়েছে কিনা তা গেমটি সনাক্ত করতে পারে।
  • গেমটি একটি গেম কন্ট্রোলারের ক্ষমতা সম্পর্কে প্রশ্ন করতে পারে।
  • গেমটি একটি গেম কন্ট্রোলার থেকে ইনকামিং মোশন ইভেন্ট চিনতে পারে।

এই পাঠের উদাহরণগুলি উপরে ডাউনলোডের জন্য উপলব্ধ নমুনা ControllerSample.zip দ্বারা প্রদত্ত রেফারেন্স বাস্তবায়নের উপর ভিত্তি করে। এই নমুনাটি দেখায় কিভাবে Android এর বিভিন্ন সংস্করণ সমর্থন করার জন্য InputManagerCompat ইন্টারফেস বাস্তবায়ন করতে হয়। নমুনা কম্পাইল করতে, আপনাকে অবশ্যই Android 4.1 (API স্তর 16) বা উচ্চতর ব্যবহার করতে হবে। একবার কম্পাইল হয়ে গেলে, স্যাম্পল অ্যাপটি বিল্ড টার্গেট হিসাবে Android 3.1 (API লেভেল 12) বা উচ্চতর চলমান যেকোনো ডিভাইসে চলে।

গেম কন্ট্রোলার সমর্থনের জন্য বিমূর্ত API গুলি প্রস্তুত করুন৷

ধরুন আপনি Android 3.1 (API স্তর 12) এ চলমান ডিভাইসগুলিতে গেম কন্ট্রোলারের সংযোগের স্থিতি পরিবর্তিত হয়েছে কিনা তা নির্ধারণ করতে সক্ষম হতে চান। যাইহোক, APIগুলি শুধুমাত্র Android 4.1 (API স্তর 16) এবং উচ্চতর সংস্করণে উপলব্ধ, তাই আপনাকে Android 4.1 এবং উচ্চতর সংস্করণকে সমর্থন করে এমন একটি বাস্তবায়ন প্রদান করতে হবে এবং একটি ফলব্যাক প্রক্রিয়া প্রদান করতে হবে যা Android 4.0 পর্যন্ত Android 3.1 সমর্থন করে৷

পুরানো সংস্করণগুলির জন্য কোন বৈশিষ্ট্যগুলির জন্য এই জাতীয় ফলব্যাক পদ্ধতির প্রয়োজন তা নির্ধারণে সহায়তা করার জন্য, টেবিল 1 Android 3.1 (API স্তর 12) এবং 4.1 (API স্তর 16) এর মধ্যে গেম কন্ট্রোলার সমর্থনের পার্থক্যগুলি তালিকাভুক্ত করে৷

সারণী 1. বিভিন্ন Android সংস্করণ জুড়ে গেম কন্ট্রোলার সমর্থনের জন্য API।

কন্ট্রোলার তথ্য কন্ট্রোলার API API স্তর 12 API স্তর 16
ডিভাইস সনাক্তকরণ getInputDeviceIds()  
getInputDevice()  
getVibrator()  
SOURCE_JOYSTICK
SOURCE_GAMEPAD
সংযোগ স্থিতি onInputDeviceAdded()
onInputDeviceChanged()
onInputDeviceRemoved()
ইনপুট ইভেন্ট সনাক্তকরণ ডি-প্যাড প্রেস ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT , KEYCODE_DPAD_RIGHT , KEYCODE_DPAD_CENTER )
গেমপ্যাড বোতাম টিপুন ( BUTTON_A , BUTTON_B , BUTTON_THUMBL , BUTTON_THUMBR , BUTTON_SELECT , BUTTON_START , BUTTON_R1 , BUTTON_L1 , BUTTON_R2 , BUTTON_L2 )
জয়স্টিক এবং হ্যাট সুইচ আন্দোলন ( AXIS_X , AXIS_Y , AXIS_Z , AXIS_RZ , AXIS_HAT_X , AXIS_HAT_Y )
এনালগ ট্রিগার প্রেস ( AXIS_LTRIGGER , AXIS_RTRIGGER )

আপনি সংস্করণ-সচেতন গেম কন্ট্রোলার সমর্থন তৈরি করতে বিমূর্ততা ব্যবহার করতে পারেন যা প্ল্যাটফর্ম জুড়ে কাজ করে। এই পদ্ধতির নিম্নলিখিত পদক্ষেপগুলি জড়িত:

  1. একটি মধ্যস্থতাকারী জাভা ইন্টারফেস সংজ্ঞায়িত করুন যা আপনার গেমের জন্য প্রয়োজনীয় গেম কন্ট্রোলার বৈশিষ্ট্যগুলির বাস্তবায়নকে বিমূর্ত করে।
  2. আপনার ইন্টারফেসের একটি প্রক্সি বাস্তবায়ন তৈরি করুন যা Android 4.1 এবং উচ্চতর সংস্করণে API ব্যবহার করে।
  3. আপনার ইন্টারফেসের একটি কাস্টম বাস্তবায়ন তৈরি করুন যা Android 3.1 থেকে Android 4.0 এর মধ্যে উপলব্ধ API ব্যবহার করে।
  4. রানটাইমে এই বাস্তবায়নের মধ্যে স্যুইচ করার জন্য যুক্তি তৈরি করুন এবং আপনার গেমে ইন্টারফেস ব্যবহার করা শুরু করুন।

অ্যান্ড্রয়েডের বিভিন্ন সংস্করণে অ্যাপ্লিকেশনগুলি একটি পশ্চাৎমুখী সামঞ্জস্যপূর্ণ উপায়ে কাজ করতে পারে তা নিশ্চিত করতে কীভাবে বিমূর্ততা ব্যবহার করা যেতে পারে তার একটি সংক্ষিপ্ত বিবরণের জন্য, পশ্চাদমুখী-সামঞ্জস্যপূর্ণ UIs তৈরি করা দেখুন।

পশ্চাদগামী সামঞ্জস্যের জন্য একটি ইন্টারফেস যোগ করুন

পশ্চাদগামী সামঞ্জস্য প্রদান করতে, আপনি একটি কাস্টম ইন্টারফেস তৈরি করতে পারেন তারপর সংস্করণ-নির্দিষ্ট বাস্তবায়ন যোগ করতে পারেন। এই পদ্ধতির একটি সুবিধা হল যে এটি আপনাকে Android 4.1 (API স্তর 16) এ পাবলিক ইন্টারফেসগুলিকে মিরর করতে দেয় যা গেম কন্ট্রোলারকে সমর্থন করে।

কোটলিন

// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
interface InputManagerCompat {
    val inputDeviceIds: IntArray
    fun getInputDevice(id: Int): InputDevice

    fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    )

    fun unregisterInputDeviceListener(listener:InputManager.InputDeviceListener)

    fun onGenericMotionEvent(event: MotionEvent)

    fun onPause()
    fun onResume()

    interface InputDeviceListener {
        fun onInputDeviceAdded(deviceId: Int)
        fun onInputDeviceChanged(deviceId: Int)
        fun onInputDeviceRemoved(deviceId: Int)
    }
}

জাভা

// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
public interface InputManagerCompat {
    ...
    public InputDevice getInputDevice(int id);
    public int[] getInputDeviceIds();

    public void registerInputDeviceListener(
            InputManagerCompat.InputDeviceListener listener,
            Handler handler);
    public void unregisterInputDeviceListener(
            InputManagerCompat.InputDeviceListener listener);

    public void onGenericMotionEvent(MotionEvent event);

    public void onPause();
    public void onResume();

    public interface InputDeviceListener {
        void onInputDeviceAdded(int deviceId);
        void onInputDeviceChanged(int deviceId);
        void onInputDeviceRemoved(int deviceId);
    }
    ...
}

InputManagerCompat ইন্টারফেস নিম্নলিখিত পদ্ধতি প্রদান করে:

getInputDevice()
মিরর getInputDevice()InputDevice অবজেক্ট পায় যা একটি গেম কন্ট্রোলারের ক্ষমতা উপস্থাপন করে।
getInputDeviceIds()
মিরর getInputDeviceIds() । পূর্ণসংখ্যার একটি অ্যারে প্রদান করে, যার প্রতিটি একটি ভিন্ন ইনপুট ডিভাইসের জন্য একটি আইডি। আপনি যদি একাধিক খেলোয়াড়কে সমর্থন করে এমন একটি গেম তৈরি করছেন এবং আপনি কতগুলি কন্ট্রোলার সংযুক্ত রয়েছে তা সনাক্ত করতে চাইলে এটি কার্যকর।
registerInputDeviceListener()
মিরর registerInputDeviceListener() । একটি নতুন ডিভাইস যোগ করা, পরিবর্তন করা বা সরানো হলে আপনাকে জানানোর জন্য নিবন্ধন করতে দেয়।
unregisterInputDeviceListener()
মিরর unregisterInputDeviceListener() । একটি ইনপুট ডিভাইস শ্রোতা নিবন্ধনমুক্ত করে।
onGenericMotionEvent()
মিরর onGenericMotionEvent() । আপনার গেমটিকে MotionEvent অবজেক্ট এবং অক্ষ মানগুলিকে আটকাতে এবং পরিচালনা করতে দেয় যা ইভেন্টগুলিকে উপস্থাপন করে যেমন জয়স্টিক মুভমেন্ট এবং এনালগ ট্রিগার প্রেস।
onPause()
গেম কন্ট্রোলার ইভেন্টের জন্য পোলিং বন্ধ করে যখন মূল অ্যাক্টিভিটি পজ করা হয়, বা যখন গেমটিতে আর ফোকাস থাকে না।
onResume()
গেম কন্ট্রোলার ইভেন্টের জন্য পোলিং শুরু করে যখন মূল অ্যাক্টিভিটি আবার শুরু হয়, বা যখন গেমটি শুরু হয় এবং ফোরগ্রাউন্ডে চলে।
InputDeviceListener
InputManager.InputDeviceListener ইন্টারফেস মিরর করে। একটি গেম কন্ট্রোলার যোগ করা, পরিবর্তন করা বা সরানো হয়েছে তা আপনার গেমকে জানাতে দেয়।

এর পরে, InputManagerCompat এর জন্য বাস্তবায়ন তৈরি করুন যা বিভিন্ন প্ল্যাটফর্ম সংস্করণ জুড়ে কাজ করে। যদি আপনার গেমটি Android 4.1 বা উচ্চতর সংস্করণে চলছে এবং একটি InputManagerCompat পদ্ধতিতে কল করে, তাহলে প্রক্সি বাস্তবায়ন InputManager সমতুল্য পদ্ধতিতে কল করে। যাইহোক, যদি আপনার গেম অ্যান্ড্রয়েড 4.0 পর্যন্ত অ্যান্ড্রয়েড 3.1 এ চলমান থাকে, তবে কাস্টম বাস্তবায়ন প্রক্রিয়াগুলি শুধুমাত্র Android 3.1 এর পরে প্রবর্তিত API ব্যবহার করে InputManagerCompat পদ্ধতিতে কল করে। রানটাইমে যে সংস্করণ-নির্দিষ্ট বাস্তবায়ন ব্যবহার করা হোক না কেন, বাস্তবায়ন কল ফলাফলগুলিকে স্বচ্ছভাবে গেমে ফিরিয়ে দেয়।

চিত্র 1. ইন্টারফেসের ক্লাস ডায়াগ্রাম এবং সংস্করণ-নির্দিষ্ট বাস্তবায়ন।

অ্যান্ড্রয়েড 4.1 এবং পরবর্তীতে ইন্টারফেস প্রয়োগ করুন

InputManagerCompatV16 হল InputManagerCompat ইন্টারফেসের একটি বাস্তবায়ন যা প্রক্সি পদ্ধতি একটি প্রকৃত InputManager এবং InputManager.InputDeviceListener কে কল করে। InputManager সিস্টেম Context থেকে প্রাপ্ত করা হয়.

কোটলিন

// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16(
        context: Context,
        private val inputManager: InputManager =
            context.getSystemService(Context.INPUT_SERVICE) as InputManager,
        private val listeners:
            MutableMap<InputManager.InputDeviceListener, V16InputDeviceListener> = mutableMapOf()
) : InputManagerCompat {
    override val inputDeviceIds: IntArray = inputManager.inputDeviceIds

    override fun getInputDevice(id: Int): InputDevice = inputManager.getInputDevice(id)

    override fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    ) {
        V16InputDeviceListener(listener).also { v16listener ->
            inputManager.registerInputDeviceListener(v16listener, handler)
            listeners += listener to v16listener
        }
    }

    // Do the same for unregistering an input device listener
    ...

    override fun onGenericMotionEvent(event: MotionEvent) {
        // unused in V16
    }

    override fun onPause() {
        // unused in V16
    }

    override fun onResume() {
        // unused in V16
    }

}

class V16InputDeviceListener(
        private val idl: InputManager.InputDeviceListener
) : InputManager.InputDeviceListener {

    override fun onInputDeviceAdded(deviceId: Int) {
        idl.onInputDeviceAdded(deviceId)
    }
    // Do the same for device change and removal
    ...
}

জাভা

// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16 implements InputManagerCompat {

    private final InputManager inputManager;
    private final Map<InputManagerCompat.InputDeviceListener,
            V16InputDeviceListener> listeners;

    public InputManagerV16(Context context) {
        inputManager = (InputManager)
                context.getSystemService(Context.INPUT_SERVICE);
        listeners = new HashMap<InputManagerCompat.InputDeviceListener,
                V16InputDeviceListener>();
    }

    @Override
    public InputDevice getInputDevice(int id) {
        return inputManager.getInputDevice(id);
    }

    @Override
    public int[] getInputDeviceIds() {
        return inputManager.getInputDeviceIds();
    }

    static class V16InputDeviceListener implements
            InputManager.InputDeviceListener {
        final InputManagerCompat.InputDeviceListener mIDL;

        public V16InputDeviceListener(InputDeviceListener idl) {
            mIDL = idl;
        }

        @Override
        public void onInputDeviceAdded(int deviceId) {
            mIDL.onInputDeviceAdded(deviceId);
        }

        // Do the same for device change and removal
        ...
    }

    @Override
    public void registerInputDeviceListener(InputDeviceListener listener,
            Handler handler) {
        V16InputDeviceListener v16Listener = new
                V16InputDeviceListener(listener);
        inputManager.registerInputDeviceListener(v16Listener, handler);
        listeners.put(listener, v16Listener);
    }

    // Do the same for unregistering an input device listener
    ...

    @Override
    public void onGenericMotionEvent(MotionEvent event) {
        // unused in V16
    }

    @Override
    public void onPause() {
        // unused in V16
    }

    @Override
    public void onResume() {
        // unused in V16
    }

}

অ্যান্ড্রয়েড 3.1 থেকে অ্যান্ড্রয়েড 4.0 পর্যন্ত ইন্টারফেস প্রয়োগ করুন

InputManagerCompat এর একটি বাস্তবায়ন তৈরি করতে যা Android 3.1 পর্যন্ত Android 4.0 সমর্থন করে, আপনি নিম্নলিখিত অবজেক্টগুলি ব্যবহার করতে পারেন:

  • ডিভাইসের সাথে সংযুক্ত গেম কন্ট্রোলার ট্র্যাক করার জন্য ডিভাইস আইডিগুলির একটি SparseArray
  • ডিভাইস ইভেন্ট প্রক্রিয়া করার জন্য একটি Handler । যখন একটি অ্যাপ শুরু বা পুনরায় চালু করা হয়, তখন Handler গেম কন্ট্রোলার সংযোগ বিচ্ছিন্ন করার জন্য পোলিং শুরু করার জন্য একটি বার্তা পায়। Handler প্রতিটি পরিচিত সংযুক্ত গেম কন্ট্রোলার চেক করার জন্য একটি লুপ শুরু করবে এবং একটি ডিভাইস আইডি ফিরে এসেছে কিনা তা দেখতে পাবে। একটি null রিটার্ন মান নির্দেশ করে যে গেম কন্ট্রোলার সংযোগ বিচ্ছিন্ন হয়েছে। অ্যাপটি পজ করা হলে Handler পোলিং বন্ধ করে দেয়।
  • InputManagerCompat.InputDeviceListener অবজেক্টের একটি Map । ট্র্যাক করা গেম কন্ট্রোলারের সংযোগ স্থিতি আপডেট করতে আপনি শ্রোতাদের ব্যবহার করবেন।

কোটলিন

// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    private val defaultHandler: Handler = PollingMessageHandler(this)
    …
}

জাভা

// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV9 implements InputManagerCompat {
    private final SparseArray<long[]> devices;
    private final Map<InputDeviceListener, Handler> listeners;
    private final Handler defaultHandler;
    …

    public InputManagerV9() {
        devices = new SparseArray<long[]>();
        listeners = new HashMap<InputDeviceListener, Handler>();
        defaultHandler = new PollingMessageHandler(this);
    }
}

একটি PollingMessageHandler অবজেক্ট প্রয়োগ করুন যা Handler প্রসারিত করে এবং handleMessage() পদ্ধতিটি ওভাররাইড করে। এই পদ্ধতিটি একটি সংযুক্ত গেম কন্ট্রোলার সংযোগ বিচ্ছিন্ন করা হয়েছে কিনা তা পরীক্ষা করে এবং নিবন্ধিত শ্রোতাদের অবহিত করে।

কোটলিন

private class PollingMessageHandler(
        inputManager: InputManagerV9,
        private val mInputManager: WeakReference<InputManagerV9> = WeakReference(inputManager)
) : Handler() {

    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)
        when (msg.what) {
            MESSAGE_TEST_FOR_DISCONNECT -> {
                mInputManager.get()?.also { imv ->
                    val time = SystemClock.elapsedRealtime()
                    val size = imv.devices.size()
                    for (i in 0 until size) {
                        imv.devices.valueAt(i)?.also { lastContact ->
                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
                                // check to see if the device has been
                                // disconnected
                                val id = imv.devices.keyAt(i)
                                if (null == InputDevice.getDevice(id)) {
                                    // Notify the registered listeners
                                    // that the game controller is disconnected
                                    imv.devices.remove(id)
                                } else {
                                    lastContact[0] = time
                                }
                            }
                        }
                    }
                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
                }
            }
        }
    }
}

জাভা

private static class PollingMessageHandler extends Handler {
    private final WeakReference<InputManagerV9> inputManager;

    PollingMessageHandler(InputManagerV9 im) {
        inputManager = new WeakReference<InputManagerV9>(im);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case MESSAGE_TEST_FOR_DISCONNECT:
                InputManagerV9 imv = inputManager.get();
                if (null != imv) {
                    long time = SystemClock.elapsedRealtime();
                    int size = imv.devices.size();
                    for (int i = 0; i < size; i++) {
                        long[] lastContact = imv.devices.valueAt(i);
                        if (null != lastContact) {
                            if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
                                // check to see if the device has been
                                // disconnected
                                int id = imv.devices.keyAt(i);
                                if (null == InputDevice.getDevice(id)) {
                                    // Notify the registered listeners
                                    // that the game controller is disconnected
                                    imv.devices.remove(id);
                                } else {
                                    lastContact[0] = time;
                                }
                            }
                        }
                    }
                    sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
                            CHECK_ELAPSED_TIME);
                }
                break;
        }
    }
}

গেম কন্ট্রোলার সংযোগ বিচ্ছিন্ন করার জন্য পোলিং শুরু করতে এবং বন্ধ করতে, এই পদ্ধতিগুলি ওভাররাইড করুন:

কোটলিন

private const val MESSAGE_TEST_FOR_DISCONNECT = 101
private const val CHECK_ELAPSED_TIME = 3000L

class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    ...
    override fun onPause() {
        defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT)
    }

    override fun onResume() {
        defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
    }
    ...
}

জাভা

private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
private static final long CHECK_ELAPSED_TIME = 3000L;

@Override
public void onPause() {
    defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
}

@Override
public void onResume() {
    defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
            CHECK_ELAPSED_TIME);
}

একটি ইনপুট ডিভাইস যোগ করা হয়েছে তা সনাক্ত করতে, onGenericMotionEvent() পদ্ধতিটি ওভাররাইড করুন। যখন সিস্টেম একটি মোশন ইভেন্ট রিপোর্ট করে, এই ইভেন্টটি ইতিমধ্যেই ট্র্যাক করা ডিভাইস আইডি থেকে এসেছে নাকি একটি নতুন ডিভাইস আইডি থেকে এসেছে তা পরীক্ষা করে দেখুন৷ ডিভাইস আইডি নতুন হলে, নিবন্ধিত শ্রোতাদের অবহিত করুন।

কোটলিন

override fun onGenericMotionEvent(event: MotionEvent) {
    // detect new devices
    val id = event.deviceId
    val timeArray: Array<Long> = mDevices.get(id) ?: run {
        // Notify the registered listeners that a game controller is added
        ...
        arrayOf<Long>().also {
            mDevices.put(id, it)
        }
    }
    timeArray[0] = SystemClock.elapsedRealtime()
}

জাভা

@Override
public void onGenericMotionEvent(MotionEvent event) {
    // detect new devices
    int id = event.getDeviceId();
    long[] timeArray = mDevices.get(id);
    if (null == timeArray) {
        // Notify the registered listeners that a game controller is added
        ...
        timeArray = new long[1];
        mDevices.put(id, timeArray);
    }
    long time = SystemClock.elapsedRealtime();
    timeArray[0] = time;
}

বার্তা সারিতে একটি DeviceEvent Runnable অবজেক্ট পাঠাতে Handler অবজেক্ট ব্যবহার করে শ্রোতাদের বিজ্ঞপ্তি প্রয়োগ করা হয়। DeviceEvent একটি InputManagerCompat.InputDeviceListener এর একটি রেফারেন্স রয়েছে। যখন DeviceEvent চলে, গেম কন্ট্রোলার যোগ করা, পরিবর্তন করা বা সরানো হয়েছে কিনা তা সংকেত দেওয়ার জন্য শ্রোতার উপযুক্ত কলব্যাক পদ্ধতিতে কল করা হয়।

কোটলিন

class InputManagerV9(
        val devices: SparseArray<Array<Long>> = SparseArray(),
        private val listeners:
        MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
    ...
    override fun registerInputDeviceListener(
            listener: InputManager.InputDeviceListener,
            handler: Handler?
    ) {
        listeners[listener] = handler ?: defaultHandler
    }

    override fun unregisterInputDeviceListener(listener: InputManager.InputDeviceListener) {
        listeners.remove(listener)
    }

    private fun notifyListeners(why: Int, deviceId: Int) {
        // the state of some device has changed
        listeners.forEach { listener, handler ->
            DeviceEvent.getDeviceEvent(why, deviceId, listener).also {
                handler?.post(it)
            }
        }
    }
    ...
}

private val sObjectQueue: Queue<DeviceEvent> = ArrayDeque<DeviceEvent>()

private class DeviceEvent(
        private var mMessageType: Int,
        private var mId: Int,
        private var mListener: InputManager.InputDeviceListener
) : Runnable {

    companion object {
        fun getDeviceEvent(messageType: Int, id: Int, listener: InputManager.InputDeviceListener) =
                sObjectQueue.poll()?.apply {
                    mMessageType = messageType
                    mId = id
                    mListener = listener
                } ?: DeviceEvent(messageType, id, listener)

    }

    override fun run() {
        when(mMessageType) {
            ON_DEVICE_ADDED -> mListener.onInputDeviceAdded(mId)
            ON_DEVICE_CHANGED -> mListener.onInputDeviceChanged(mId)
            ON_DEVICE_REMOVED -> mListener.onInputDeviceChanged(mId)
            else -> {
                // Handle unknown message type
            }
        }
    }

}

জাভা

@Override
public void registerInputDeviceListener(InputDeviceListener listener,
        Handler handler) {
    listeners.remove(listener);
    if (handler == null) {
        handler = defaultHandler;
    }
    listeners.put(listener, handler);
}

@Override
public void unregisterInputDeviceListener(InputDeviceListener listener) {
    listeners.remove(listener);
}

private void notifyListeners(int why, int deviceId) {
    // the state of some device has changed
    if (!listeners.isEmpty()) {
        for (InputDeviceListener listener : listeners.keySet()) {
            Handler handler = listeners.get(listener);
            DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
                    listener);
            handler.post(odc);
        }
    }
}

private static class DeviceEvent implements Runnable {
    private int mMessageType;
    private int mId;
    private InputDeviceListener mListener;
    private static Queue<DeviceEvent> sObjectQueue =
            new ArrayDeque<DeviceEvent>();
    ...

    static DeviceEvent getDeviceEvent(int messageType, int id,
            InputDeviceListener listener) {
        DeviceEvent curChanged = sObjectQueue.poll();
        if (null == curChanged) {
            curChanged = new DeviceEvent();
        }
        curChanged.mMessageType = messageType;
        curChanged.mId = id;
        curChanged.mListener = listener;
        return curChanged;
    }

    @Override
    public void run() {
        switch (mMessageType) {
            case ON_DEVICE_ADDED:
                mListener.onInputDeviceAdded(mId);
                break;
            case ON_DEVICE_CHANGED:
                mListener.onInputDeviceChanged(mId);
                break;
            case ON_DEVICE_REMOVED:
                mListener.onInputDeviceRemoved(mId);
                break;
            default:
                // Handle unknown message type
                ...
                break;
        }
        // Put this runnable back in the queue
        sObjectQueue.offer(this);
    }
}

আপনার কাছে এখন InputManagerCompat এর দুটি বাস্তবায়ন রয়েছে: একটি যেটি Android 4.1 এবং উচ্চতর সংস্করণে চলমান ডিভাইসগুলিতে কাজ করে এবং অন্যটি Android 4.0 পর্যন্ত Android 3.1 চালিত ডিভাইসগুলিতে কাজ করে৷

সংস্করণ-নির্দিষ্ট বাস্তবায়ন ব্যবহার করুন

সংস্করণ-নির্দিষ্ট স্যুইচিং লজিক এমন একটি শ্রেণিতে প্রয়োগ করা হয় যা একটি কারখানা হিসাবে কাজ করে।

কোটলিন

object Factory {
    fun getInputManager(context: Context): InputManagerCompat =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                InputManagerV16(context)
            } else {
                InputManagerV9()
            }
}

জাভা

public static class Factory {
    public static InputManagerCompat getInputManager(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            return new InputManagerV16(context);
        } else {
            return new InputManagerV9();
        }
    }
}

এখন আপনি সহজভাবে একটি InputManagerCompat অবজেক্ট ইনস্ট্যান্টিয়েট করতে পারেন এবং আপনার প্রধান View একটি InputManagerCompat.InputDeviceListener নিবন্ধন করতে পারেন। আপনার সেট আপ করা সংস্করণ-স্যুইচিং যুক্তির কারণে, আপনার গেমটি স্বয়ংক্রিয়ভাবে প্রয়োগটি ব্যবহার করে যা ডিভাইসটি চলমান Android এর সংস্করণের জন্য উপযুক্ত।

কোটলিন

class GameView(context: Context) : View(context), InputManager.InputDeviceListener {
    private val inputManager: InputManagerCompat = Factory.getInputManager(context).apply {
        registerInputDeviceListener(this@GameView, null)
        ...
    }
    ...
}

জাভা

public class GameView extends View implements InputDeviceListener {
    private InputManagerCompat inputManager;
    ...

    public GameView(Context context, AttributeSet attrs) {
        inputManager =
                InputManagerCompat.Factory.getInputManager(this.getContext());
        inputManager.registerInputDeviceListener(this, null);
        ...
    }
}

এরপর, আপনার প্রধান ভিউতে onGenericMotionEvent() পদ্ধতিটিকে ওভাররাইড করুন, যেমনটি বর্ণনা করা হয়েছে একটি গেম কন্ট্রোলার থেকে একটি মোশন ইভেন্ট পরিচালনা করুন । আপনার গেমটি এখন Android 3.1 (API স্তর 12) এবং উচ্চতর চলমান ডিভাইসগুলিতে ধারাবাহিকভাবে গেম কন্ট্রোলার ইভেন্টগুলি প্রক্রিয়া করতে সক্ষম হওয়া উচিত।

কোটলিন

override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    inputManager.onGenericMotionEvent(event)

    // Handle analog input from the controller as normal
    ...
    return super.onGenericMotionEvent(event)
}

জাভা

@Override
public boolean onGenericMotionEvent(MotionEvent event) {
    inputManager.onGenericMotionEvent(event);

    // Handle analog input from the controller as normal
    ...
    return super.onGenericMotionEvent(event);
}

উপরের ডাউনলোডের জন্য উপলব্ধ নমুনা ControllerSample.zip এ প্রদত্ত GameView ক্লাসে আপনি এই সামঞ্জস্যপূর্ণ কোডটির সম্পূর্ণ বাস্তবায়ন খুঁজে পেতে পারেন।