SDK customers can now access the documentation publicly without login. The portal runs independently from admin-v2 on https://macmini:3006/. - New developer-portal/ app with 26 pages, 2 components - Docker service + nginx SSL reverse proxy on port 3006 - All /developers/* routes remapped to /* in the new app - admin-v2 developer pages remain unchanged Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
270 lines
9.5 KiB
TypeScript
270 lines
9.5 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
import { SDKDocsSidebar } from '@/components/SDKDocsSidebar'
|
|
import { Copy, Check, Smartphone } from 'lucide-react'
|
|
|
|
function CopyButton({ text }: { text: string }) {
|
|
const [copied, setCopied] = useState(false)
|
|
const handleCopy = async () => {
|
|
await navigator.clipboard.writeText(text)
|
|
setCopied(true)
|
|
setTimeout(() => setCopied(false), 2000)
|
|
}
|
|
return (
|
|
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
|
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
|
return (
|
|
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
|
{filename && (
|
|
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
|
{filename}
|
|
</div>
|
|
)}
|
|
<div className="relative">
|
|
<div className="absolute top-2 right-2">
|
|
<CopyButton text={code} />
|
|
</div>
|
|
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
|
<code>{code}</code>
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function AndroidSDKPage() {
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex">
|
|
<SDKDocsSidebar />
|
|
|
|
<main className="flex-1 ml-64">
|
|
<div className="max-w-4xl mx-auto px-8 py-12">
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<div className="w-10 h-10 rounded-lg bg-green-600 flex items-center justify-center">
|
|
<Smartphone className="w-6 h-6 text-white" />
|
|
</div>
|
|
<h1 className="text-3xl font-bold text-gray-900">Android SDK (Kotlin)</h1>
|
|
</div>
|
|
<p className="text-lg text-gray-600 mb-8">
|
|
Native Kotlin SDK fuer Android API 26+ mit Jetpack Compose Unterstuetzung.
|
|
</p>
|
|
|
|
{/* Requirements */}
|
|
<section className="mb-12">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Systemvoraussetzungen</h2>
|
|
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
|
<table className="min-w-full divide-y divide-gray-200">
|
|
<tbody className="divide-y divide-gray-200">
|
|
<tr>
|
|
<td className="px-6 py-4 text-sm font-medium text-gray-900">Kotlin Version</td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">1.9+</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4 text-sm font-medium text-gray-900">Min SDK</td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">API 26 (Android 8.0)</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4 text-sm font-medium text-gray-900">Compile SDK</td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">34+</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Installation */}
|
|
<section className="mb-12">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
|
<CodeBlock
|
|
filename="build.gradle.kts (Module)"
|
|
code={`dependencies {
|
|
implementation("com.breakpilot:consent-sdk:1.0.0")
|
|
|
|
// Fuer Jetpack Compose
|
|
implementation("com.breakpilot:consent-sdk-compose:1.0.0")
|
|
}`}
|
|
/>
|
|
</section>
|
|
|
|
{/* Basic Setup */}
|
|
<section className="mb-12">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Grundlegende Einrichtung</h2>
|
|
<CodeBlock
|
|
filename="MyApplication.kt"
|
|
code={`import android.app.Application
|
|
import com.breakpilot.consent.ConsentManager
|
|
|
|
class MyApplication : Application() {
|
|
override fun onCreate() {
|
|
super.onCreate()
|
|
|
|
// Consent Manager konfigurieren
|
|
ConsentManager.configure(
|
|
context = this,
|
|
config = ConsentConfig(
|
|
apiEndpoint = "https://api.example.com/consent",
|
|
siteId = "my-android-app"
|
|
)
|
|
)
|
|
|
|
// Initialisieren
|
|
lifecycleScope.launch {
|
|
ConsentManager.initialize()
|
|
}
|
|
}
|
|
}`}
|
|
/>
|
|
</section>
|
|
|
|
{/* Jetpack Compose */}
|
|
<section className="mb-12">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Jetpack Compose Integration</h2>
|
|
<CodeBlock
|
|
filename="MainActivity.kt"
|
|
code={`import androidx.compose.runtime.*
|
|
import com.breakpilot.consent.compose.*
|
|
|
|
@Composable
|
|
fun MainScreen() {
|
|
val consent = rememberConsentState()
|
|
|
|
Column {
|
|
// Consent-abhaengige UI
|
|
if (consent.hasConsent(ConsentCategory.ANALYTICS)) {
|
|
AnalyticsView()
|
|
}
|
|
|
|
// Buttons
|
|
Button(onClick = { consent.acceptAll() }) {
|
|
Text("Alle akzeptieren")
|
|
}
|
|
|
|
Button(onClick = { consent.rejectAll() }) {
|
|
Text("Alle ablehnen")
|
|
}
|
|
}
|
|
|
|
// Consent Banner (automatisch angezeigt wenn noetig)
|
|
ConsentBanner()
|
|
}
|
|
|
|
// ConsentGate Composable
|
|
@Composable
|
|
fun ProtectedContent() {
|
|
ConsentGate(
|
|
category = ConsentCategory.MARKETING,
|
|
fallback = {
|
|
Text("Marketing-Zustimmung erforderlich")
|
|
}
|
|
) {
|
|
MarketingContent()
|
|
}
|
|
}`}
|
|
/>
|
|
</section>
|
|
|
|
{/* Traditional Android */}
|
|
<section className="mb-12">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">View-basierte Integration</h2>
|
|
<CodeBlock
|
|
filename="MainActivity.kt"
|
|
code={`import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.lifecycle.lifecycleScope
|
|
import com.breakpilot.consent.ConsentManager
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.flow.collect
|
|
|
|
class MainActivity : AppCompatActivity() {
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
setContentView(R.layout.activity_main)
|
|
|
|
// Auf Consent-Aenderungen reagieren
|
|
lifecycleScope.launch {
|
|
ConsentManager.consentFlow.collect { state ->
|
|
updateUI(state)
|
|
}
|
|
}
|
|
|
|
// Banner anzeigen wenn noetig
|
|
if (ConsentManager.needsConsent()) {
|
|
ConsentManager.showBanner(this)
|
|
}
|
|
}
|
|
|
|
private fun updateUI(state: ConsentState?) {
|
|
if (state?.hasConsent(ConsentCategory.ANALYTICS) == true) {
|
|
loadAnalytics()
|
|
}
|
|
}
|
|
|
|
fun onAcceptAllClick(view: View) {
|
|
lifecycleScope.launch {
|
|
ConsentManager.acceptAll()
|
|
}
|
|
}
|
|
}`}
|
|
/>
|
|
</section>
|
|
|
|
{/* API Reference */}
|
|
<section>
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">API Referenz</h2>
|
|
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
|
<table className="min-w-full divide-y divide-gray-200">
|
|
<thead className="bg-gray-50">
|
|
<tr>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Methode</th>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Beschreibung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-gray-200">
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">configure()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">SDK konfigurieren</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">initialize()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">SDK initialisieren (suspend)</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">hasConsent()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Consent fuer Kategorie pruefen</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">consentFlow</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Flow fuer reaktive Updates</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">acceptAll()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Alle akzeptieren (suspend)</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">rejectAll()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Alle ablehnen (suspend)</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">setConsent()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Kategorien setzen (suspend)</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="px-6 py-4"><code className="text-violet-600">showBanner()</code></td>
|
|
<td className="px-6 py-4 text-sm text-gray-600">Banner als DialogFragment</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|