Öne çıkarılmış kurulum ile devam et (yalnızca Android için)

Bu makale, uygulamalarınızda Süreklilik SDK'sını kullanarak özellikleri tümleştirme konusunda birinci taraf ve üçüncü taraf geliştiriciler için kapsamlı yönergeler sağlar. Süreklilik SDK'sı sorunsuz cihazlar arası deneyimler sağlayarak kullanıcıların Android ve Windows gibi farklı platformlarda etkinlikleri sürdürmesine olanak tanır.

Bu kılavuzu izleyerek, Süreklilik SDK'sını kullanarak Windows Özgeçmiş'i kullanarak birden çok cihazda sorunsuz ve tümleşik bir kullanıcı deneyimi oluşturabilirsiniz.

Önemli

Windows'ta Uygulama Başlangıcı

Özgeçmiş Sınırlı Erişim Özelliğidir (LAF). Bu API'ye access kazanmak için Microsoft'tan Android mobil cihazlarda "Windows Bağlantısı" paketiyle birlikte çalışma onayı almanız gerekir.

Erişim talep etmek için, aşağıda listelenen bilgileri içeren bir e-posta wincrossdeviceapi@microsoft.com adresine gönderin.

  • Kullanıcı deneyiminizin açıklaması
  • Kullanıcının web'e veya belgelere yerel olarak eriştiği uygulamanızın ekran görüntüsü
  • Uygulamanızın PackageId değeri
  • Uygulamanızın Google Play store URL'si

İstek onaylanırsa özelliğin kilidini açma yönergelerini alırsınız. Onaylar, senaryonuzun ana hatlarıyla belirtilen Senaryo Gereksinimlerini karşılaması koşuluyla iletişiminizi temel alır.

Önkoşullar

Android uygulamaları için Süreklilik SDK'sını tümleştirmeden önce aşağıdaki gereksinimlerin karşılandığından emin olun:

  • En Düşük SDK Sürümü: 24
  • Kotlin Sürümü: 1.9.x
  • Windows bağlantısı (LTW): 1.241101.XX

Windows uygulamaları için aşağıdaki gereksinimlerin karşılandığından emin olun:

  • En Düşük Windows Sürümü: Windows 11
  • Geliştirme Ortamı: Visual Studio 2019 veya üzeri

Uyarı

iOS uygulamaları şu anda Süreklilik SDK'sı ile tümleştirme için desteklenmiyor.

Geliştirme ortamınızı yapılandırma

Aşağıdaki bölümlerde hem Android hem de Windows uygulamaları için geliştirme ortamını ayarlamaya yönelik adım adım yönergeler sağlanır.

Android kurulumu

Android için geliştirme ortamını ayarlamak için şu adımları izleyin:

  1. Paketi ayarlamak için aşağıdaki sürümlerde sağlanan kitaplıklar aracılığıyla .aar dosyasını indirin ve kullanın: Windows Cihazlar Arası SDK sürümleri.

  2. Meta etiketleri Android uygulamanızın AndroidManifest.xml dosyasına ekleyin. Aşağıdaki kod parçacığında gerekli meta etiketlerin nasıl ekleneceği gösterilmektedir:

    <meta-data 
        android:name="com.microsoft.crossdevice.resumeActivityProvider" 
        android:value="true" /> 
    
    <meta-data 
        android:name="com.microsoft.crossdevice.trigger.PartnerApp" 
        android:value="4" /> 
    

API tümleştirme adımları

Bildirim bildirimlerinden sonra, uygulama geliştiricileri basit bir kod örneğini izleyerek uygulama bağlamlarını kolayca gönderebilir.

Uygulamanın yapması gerekenler:

  1. Süreklilik SDK'sını Başlatma/Kaldırma:
    1. Uygulamanın Initialize ve DeInitialize işlevlerini çağırmak için uygun zamanı belirlemesi gerekir.
    2. Initialize işlevi çağrıldıktan sonra, IAppContextEventHandler uygulayan bir geri çağırma tetiklenmelidir.
  2. AppContext Gönderme/Silme:
    1. SDK'yı başlatdıktan sonra onContextRequestReceived çağrılırsa bağlantının kurulduğunu gösterir. Uygulama daha sonra APPContext'i LTW'ye gönderebilir (oluşturma ve güncelleştirme dahil) veya APPContext'i LTW'den silebilir.
    2. Telefon ve bilgisayar arasında bağlantı yoksa ve uygulama LTW'ye AppContext gönderirse, uygulama "Bilgisayar bağlı değil" iletisiyle onContextResponseError alır.
    3. Bağlantı yeniden kurulduğunda , onContextRequestReceived yeniden çağrılır. Uygulama daha sonra geçerli AppContext'i LTW'ye gönderebilir.
    4. onSyncServiceDisconnected veya SDK'yı kaldırdıktan sonra uygulama appContext göndermemelidir.

Aşağıda bir kod örneği verilmiştir. AppContext'te gerekli ve isteğe bağlı tüm alanlar için lütfen AppContext açıklamasına bakın.

Aşağıdaki Android kod parçacığı, Süreklilik SDK'sını kullanarak API isteklerinin nasıl yapılacağını gösterir:

import android.os.Bundle 
import android.util.Log 
import android.widget.Button 
import android.widget.TextView 
import android.widget.Toast 
import androidx.activity.enableEdgeToEdge 
import androidx.appcompat.app.AppCompatActivity 
import androidx.core.view.ViewCompat 
import androidx.core.view.WindowInsetsCompat 
import androidx.lifecycle.LiveData 
import androidx.lifecycle.MutableLiveData 
import androidx.lifecycle.Observer 
import com.microsoft.crossdevicesdk.continuity.AppContext 
import com.microsoft.crossdevicesdk.continuity.AppContextManager 
import com.microsoft.crossdevicesdk.continuity.ContextRequestInfo 
import com.microsoft.crossdevicesdk.continuity.IAppContextEventHandler 
import com.microsoft.crossdevicesdk.continuity.IAppContextResponse 
import com.microsoft.crossdevicesdk.continuity.LogUtils 
import com.microsoft.crossdevicesdk.continuity.ProtocolConstants 
import java.util.UUID 

  

class MainActivity : AppCompatActivity() { 

    //Make buttons member variables --- 
    private lateinit var buttonSend: Button 
    private lateinit var buttonDelete: Button 
    private lateinit var buttonUpdate: Button 

    private val appContextResponse = object : IAppContextResponse { 
        override fun onContextResponseSuccess(response: AppContext) { 
            Log.d("MainActivity", "onContextResponseSuccess") 
            runOnUiThread { 
                Toast.makeText( 
                    this@MainActivity, 
                    "Context response success: ${response.contextId}", 
                    Toast.LENGTH_SHORT 
                ).show() 
            } 
        } 

        override fun onContextResponseError(response: AppContext, throwable: Throwable) { 
            Log.d("MainActivity", "onContextResponseError: ${throwable.message}") 
            runOnUiThread { 
                Toast.makeText( 
                    this@MainActivity, 
                    "Context response error: ${throwable.message}", 
                    Toast.LENGTH_SHORT 
                ).show() 

                // Check if the error message contains the specific string 
                if (throwable.message?.contains("PC is not connected") == true) { 
                    //App should stop sending intent once this callback is received 
                }
            } 
        } 
    } 

    private lateinit var appContextEventHandler: IAppContextEventHandler 

    private val _currentAppContext = MutableLiveData<AppContext?>() 

    private val currentAppContext: LiveData<AppContext?> get() = _currentAppContext 

    override fun onCreate(savedInstanceState: Bundle?) { 
        super.onCreate(savedInstanceState) 
        enableEdgeToEdge() 
        setContentView(R.layout.activity_main) 
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> 
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) 
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) 
            insets 
        } 

        LogUtils.setDebugMode(true) 
        var ready = false 
        buttonSend = findViewById(R.id.buttonSend) 
        buttonDelete = findViewById(R.id.buttonDelete) 
        buttonUpdate = findViewById(R.id.buttonUpdate) 
        setButtonDisabled(buttonSend) 
        setButtonDisabled(buttonDelete) 
        setButtonDisabled(buttonUpdate) 

        buttonSend.setOnClickListener { 
            if (ready) { 
                sendResumeActivity() 
            } 
        } 

        buttonDelete.setOnClickListener { 
            if (ready) { 
                deleteResumeActivity() 
            } 
        } 

        buttonUpdate.setOnClickListener { 
            if (ready) { 
                updateResumeActivity() 
            }
        } 

        appContextEventHandler = object : IAppContextEventHandler { 

            override fun onContextRequestReceived(contextRequestInfo: ContextRequestInfo) { 
                LogUtils.d("MainActivity", "onContextRequestReceived") 
                ready = true 
                setButtonEnabled(buttonSend) 
                setButtonEnabled(buttonDelete) 
                setButtonEnabled(buttonUpdate) 

            } 

  

            override fun onInvalidContextRequestReceived(throwable: Throwable) { 
                Log.d("MainActivity", "onInvalidContextRequestReceived") 

            } 

  

            override fun onSyncServiceDisconnected() { 
                Log.d("MainActivity", "onSyncServiceDisconnected") 
                ready = false 
                setButtonDisabled(buttonSend) 
                setButtonDisabled(buttonDelete) 
            } 
        } 

        // Initialize the AppContextManager 
        AppContextManager.initialize(this.applicationContext, appContextEventHandler) 

        // Update currentAppContext text view. 
        val textView = findViewById<TextView>(R.id.appContext) 

        currentAppContext.observe(this, Observer { appContext -> 
            appContext?.let { 
                textView.text = 
                    "Current app context: ${it.contextId}\n App ID: ${it.appId}\n Created: ${it.createTime}\n Updated: ${it.lastUpdatedTime}\n Type: ${it.type}" 
                Log.d("MainActivity", "Current app context: ${it.contextId}") 
            } ?: run { 
                textView.text = "No current app context available" 
                Log.d("MainActivity", "No current app context available") 
            } 
        }) 
    } 


    // Send resume activity to LTW 
    private fun sendResumeActivity() { 
        val appContext = AppContext().apply { 
            this.contextId = generateContextId() 
            this.appId = applicationContext.packageName 
            this.createTime = System.currentTimeMillis() 
            this.lastUpdatedTime = System.currentTimeMillis() 
            this.type = ProtocolConstants.TYPE_RESUME_ACTIVITY 
        } 

        _currentAppContext.value = appContext 
        AppContextManager.sendAppContext(this.applicationContext, appContext, appContextResponse) 
    } 

    // Delete resume activity from LTW 
    private fun deleteResumeActivity() { 
        currentAppContext.value?.let { 
            AppContextManager.deleteAppContext( 
                this.applicationContext, 
                it.contextId, 
                appContextResponse 
            ) 
            _currentAppContext.value = null 
        } ?: run { 
            Toast.makeText(this, "No resume activity to delete", Toast.LENGTH_SHORT).show() 
            Log.d("MainActivity", "No resume activity to delete") 
        }
    } 

    private fun updateResumeActivity() { 
        currentAppContext.value?.let { 
            it.lastUpdatedTime = System.currentTimeMillis() 
            AppContextManager.sendAppContext(this.applicationContext, it, appContextResponse) 
            _currentAppContext.postValue(it) 
        } ?: run { 
            Toast.makeText(this, "No resume activity to update", Toast.LENGTH_SHORT).show() 
            Log.d("MainActivity", "No resume activity to update") 
        } 
    } 

    private fun setButtonDisabled(button: Button) { 
        button.isEnabled = false 
        button.alpha = 0.5f 
    } 

    private fun setButtonEnabled(button: Button) { 
        button.isEnabled = true 
        button.alpha = 1.0f 
    } 

    override fun onDestroy() { 
        super.onDestroy() 
        // Deinitialize the AppContextManager 
        AppContextManager.deInitialize(this.applicationContext) 
    } 

    override fun onStart() { 
        super.onStart() 
        // AppContextManager.initialize(this.applicationContext, appContextEventHandler) 
    } 


    override fun onStop() { 
        super.onStop() 
        // AppContextManager.deInitialize(this.applicationContext) 
    } 

    private fun generateContextId(): String { 
        return "${packageName}.${UUID.randomUUID()}" 
    } 

} 

Tümleştirme doğrulama adımları

Süreklilik SDK'sının uygulamanızda tümleştirmesini doğrulamak için şu adımları izleyin:

Hazırlık

Tümleştirme doğrulamasına hazırlanmak için aşağıdaki adımlar gereklidir:

  1. Özel LTW'nin yüklendiğinden emin olun.

  2. LTW'yi bilgisayarınıza bağlayın:

    Yönergeler için bkz. Bilgisayarınızda mobil cihazınızı yönetme .

    Uyarı

    QR kodunu taradıktan sonra LTW'ye yönlendirilmezseniz, lütfen önce LTW'yi açın ve uygulama içindeki QR kodunu tarayın.

  3. İş ortağı uygulamasının Süreklilik SDK'sını tümleştirdiğini doğrulayın.

Validation

Ardından, tümleştirmeyi doğrulamak için şu adımları izleyin:

  1. Uygulamayı başlatın ve SDK'yi başlatın. onContextRequestReceived öğesinin çağrıldığını onaylayın.
  2. onContextRequestReceived çağrıldıktan sonra uygulama AppContext'i LTW'ye gönderebilir. AppContext'i gönderdikten sonra onContextResponseSuccess çağrılırsa SDK tümleştirmesi başarılı olur.
  3. Uygulama, bilgisayar kilitli veya bağlantısı kesilmiş durumdayken AppContext gönderirse, onContextResponseError'ın "Bilgisayar bağlı değil" ile çağrıldığını doğrulayın.
  4. Bağlantı geri yüklendiğinde onContextRequestReceived öğesinin yeniden çağrıldığından ve uygulamanın geçerli AppContext'i LTW'ye gönderebildiğinden emin olun.

Aşağıdaki ekran görüntüsünde, bilgisayarın bağlantısı "Bilgisayar bağlı değil" hata iletisiyle kesildiğinde günlük girdisi ve onContextRequestReceived yeniden çağrıldığında yeniden bağlandıktan sonra günlük girişi gösterilir.

Bilgisayarın bağlı olmadığını gösteren hata mesajını ve yeniden bağlandıktan sonra ortaya çıkan onContextRequestReceived günlük girdisini içeren Windows günlük girişlerinin ekran görüntüsü.

AppContext

Windows Resume, AppContext'i, Windows Resume'un hangi uygulamanın devam ettirileceğini ve uygulamanın hangi bağlamda devam ettirileceğini anlayabilmesini sağlayan meta veriler olarak tanımlar. Uygulamalar, kullanıcıların uygulamalarında yaptıklarına birden çok cihazda geri dönmelerini sağlamak için etkinlikleri kullanabilir. Herhangi bir mobil uygulama tarafından oluşturulan etkinlikler, bu cihazlar Cihazlar Arası Deneyim Konağı (CDEH) ile donatıldığında kullanıcıların Windows cihazlarında görünür.

Her uygulama farklıdır ve özgeçmiş için hedef uygulamayı ve bağlamı anlamak için Windows'ta belirli uygulamalara kadar olan hedef uygulamayı anlamak Windows'a bağlıdır. Windows Özgeçmiş, tüm birinci taraf ve üçüncü taraf uygulama özgeçmiş senaryoları için gereksinimleri karşılayan genel bir şema kullanır.

contextId

  • Gerekli: Evet
  • Açıklama: Bu, bir AppContext'i diğerinden ayırmak için kullanılan benzersiz bir tanımlayıcıdır. Her AppContext'in benzersiz bir şekilde tanımlanabilir olmasını sağlar.
  • Kullanım: Çakışmaları önlemek için her AppContext için benzersiz bir contextId oluşturduğunuzdan emin olun.

type

  • Gerekli: Evet
  • Açıklama: Bu, Windows Bağlantısı'na (LTW) gönderilen AppContext türünü gösteren ikili bir bayraktır. Değerin requestedContextType ile tutarlı olması gerekir.
  • Kullanım: Bu bayrağı gönderdiğiniz bağlam türüne göre ayarlayın. Örneğin, ProtocolConstants.TYPE_RESUME_ACTIVITY.

createTime

  • Gerekli: Evet
  • Açıklama: Bu zaman damgası AppContext'in oluşturma zamanını temsil eder.
  • Kullanım: AppContext'in oluşturulduğu zamanı tam olarak kaydedin.

intentUri

  • Gerekli: Hayır, web bağlantısı sağlanmışsa
  • Açıklama: Bu URI, kaynak cihazdan teslim edilen AppContext'e hangi uygulamanın devamebileceğini gösterir.
  • Kullanım: Bağlamı işlemek için belirli bir uygulama belirtmek istiyorsanız bunu sağlayın.
  • Gerekli mi: Hayır, eğer intentUri sağlanmışsa
  • Açıklama: Bu URI, mağaza uygulamalarını kullanmamayı seçerse uygulamanın web uç noktasını başlatmak için kullanılır. Bu parametre yalnızca intentUri sağlanmadığında kullanılır. Her ikisi de sağlanmışsa, uygulamayı Windows'ta sürdürmek için intentUri kullanılır.
  • Kullanım: Yalnızca uygulama web uç noktalarında devam etmek isteyip mağaza uygulamalarını yeniden başlatmak istemiyorsa kullanılabilir.

appId (uygulama kimliği)

  • Gerekli: Evet
  • Açıklama: Bu, bağlamın olduğu uygulamanın paket adıdır.
  • Kullanım: Bunu uygulamanızın paket adı olarak ayarlayın.

title

  • Gerekli: Evet
  • Açıklama: Bu, belge adı veya web sayfası başlığı gibi AppContext'in başlığıdır.
  • Kullanım: AppContext'i temsil eden anlamlı bir başlık sağlayın.

Önizleme

  • Gerekli: Hayır
  • Açıklama: Bunlar AppContext'i temsil eden önizleme görüntüsünün baytlarıdır.
  • Kullanım: Kullanıcılara AppContext'in görsel bir gösterimini sağlamak için varsa bir önizleme görüntüsü sağlayın.

Ömür

  • Gerekli: Hayır
  • Açıklama: Bu, milisaniye cinsinden yaşam süresidir AppContext . Yalnızca devam eden senaryolar için kullanılır. Ayarlanmadıysa, varsayılan değer 5 dakikadır.
  • Kullanım: Geçerli olması gereken zamanı AppContext tanımlamak için bunu ayarlayın. En fazla 5 dakikaya kadar bir değer ayarlayabilirsiniz. Daha büyük değerler otomatik olarak 5 dakikaya kısaltılır.

Amaç URI'leri

URI'ler, belirli bir görevi gerçekleştirmek için başka bir uygulama başlatmanıza olanak tanıyarak uygulamadan uygulamaya senaryolara yardımcı olur. URI'leri kullanarak uygulama başlatma hakkında daha fazla bilgi için Bir URI için Varsayılan Windows Uygulamasını Başlatma ve Uygulama İçeriğine Derin Bağlantılar Oluşturma | Android Geliştiriciler belgelerine bakın.

Windows'da API yanıtlarını işleme

Bu bölümde, Windows uygulamalarında API yanıtlarının nasıl işleneceğini açıklanmaktadır. Süreklilik SDK'sı, Win32 ve WinUI uygulamaları için API yanıtlarını işlemek için bir yol sağlar.

Win32 uygulama örneği

Win32 uygulamalarının protokol URI başlatma işlemini işlemesi için aşağıdaki adımlar gereklidir:

  1. İlk olarak, kayıt defterine aşağıdaki gibi bir giriş yapılması gerekir:

    [HKEY_CLASSES_ROOT\partnerapp] 
    @="URL:PartnerApp Protocol" 
    "URL Protocol"="" 
    
    [HKEY_CLASSES_ROOT\partnerapp\shell\open\command] 
    @="\"C:\\path\\to\\PartnerAppExecutable.exe\" \"%1\"" 
    
  2. Başlatma, Win32 uygulamasının ana işlevinde işlenmelidir:

    #include <windows.h> 
    #include <shellapi.h> 
    #include <string> 
    #include <iostream> 
    
    int CALLBACK wWinMain(HINSTANCE, HINSTANCE, PWSTR lpCmdLine, int) 
    { 
        // Check if there's an argument passed via lpCmdLine 
        std::wstring cmdLine(lpCmdLine); 
        std::wstring arguments; 
    
        if (!cmdLine.empty()) 
        { 
            // Check if the command-line argument starts with "partnerapp://", indicating a URI launch 
            if (cmdLine.find(L"partnerapp://") == 0) 
            { 
                // This is a URI protocol launch 
                // Process the URI as needed 
                // Example: Extract action and parameters from the URI 
                arguments = cmdLine;  // or further parse as required 
            } 
            else 
            {
                // Launched by command line or activation APIs 
            } 
        } 
        else 
        { 
            // Handle cases where no arguments were passed 
        } 
    
        return 0; 
    } 
    

WinUI uygulamaları

Paketlenmiş WinUI uygulamaları için protokol URI'si projenin uygulama bildirimine kaydedilebilir. Aşağıdaki adımlarda bir WinUI uygulamasında protokol etkinleştirmenin nasıl işleneceğini gösterilmektedir.

  1. İlk olarak, protokol URI'si dosyaya Package.appxmanifest aşağıdaki gibi kaydedilir:

    <Applications> 
            <Application Id= ... > 
                <Extensions> 
                    <uap:Extension Category="windows.protocol"> 
                      <uap:Protocol Name="alsdk"> 
                        <uap:Logo>images\icon.png</uap:Logo> 
                        <uap:DisplayName>SDK Sample URI Scheme</uap:DisplayName> 
                      </uap:Protocol> 
                    </uap:Extension> 
              </Extensions> 
              ... 
            </Application> 
       <Applications> 
    

WinUI 3 örneği

Aşağıdaki kod parçacığı, Windows Uygulama SDK'sı ile bir C++ WinUI uygulamasında protokol etkinleştirmenin nasıl işleneceğini gösterir:

void App::OnActivated(winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) 
{ 
     if (args.Kind() == winrt::Windows::ApplicationModel::Activation::ActivationKind::Protocol) 
     { 
         auto protocolArgs = args.as<winrt::Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs>(); 
         auto uri = protocolArgs.Uri(); 
         std::wstring uriString = uri.AbsoluteUri().c_str(); 
         //Process the URI as per argument scheme 
     } 
} 

Web bağlantısı kullanıldığında uygulamanın web uç noktası başlatılır. Uygulama geliştiricilerin Android uygulamasından sağlanan web bağlantısının geçerli olduğundan emin olmaları gerekir çünkü Windows Özgeçmiş, sağlanan web bağlantısına yeniden yönlendirmek için sistemin varsayılan tarayıcısını kullanır.

Windows Özgeçmiş'ten alınan bağımsız değişkenleri işleme

Her uygulamanın, alınan bağımsız değişkeni seri durumdan çıkarmak ve şifresini çözmek, ardından devam eden veriyi telefondan bilgisayara aktarmak için bilgileri buna göre işlemesi gerekliliği vardır. Örneğin, bir çağrının aktarılması gerekiyorsa uygulamanın bu bağlamı telefondan iletişim kurabilmesi ve masaüstü uygulamasının bu bağlamı uygun şekilde anlayıp yüklemeye devam etmesi gerekir.