5package io.flutter.plugin.platform;
7import static io.flutter.Build.API_LEVELS;
10import android.app.ActivityManager.TaskDescription;
12import android.content.ClipDescription;
13import android.content.ClipboardManager;
16import android.content.res.AssetFileDescriptor;
19import android.view.HapticFeedbackConstants;
20import android.view.SoundEffectConstants;
23import android.view.WindowManager;
24import androidx.activity.OnBackPressedDispatcherOwner;
25import androidx.annotation.NonNull;
26import androidx.annotation.Nullable;
27import androidx.annotation.VisibleForTesting;
28import androidx.core.view.WindowInsetsControllerCompat;
30import io.flutter.embedding.engine.systemchannels.PlatformChannel;
31import java.io.FileNotFoundException;
32import java.io.IOException;
38 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
40 private final Activity activity;
43 private PlatformChannel.SystemChromeStyle currentTheme;
44 private int mEnabledOverlays;
45 private static final String TAG =
"PlatformPlugin";
90 public void setPreferredOrientations(
int androidOrientation) {
91 setSystemChromePreferredOrientations(androidOrientation);
95 public void setApplicationSwitcherDescription(
97 setSystemChromeApplicationSwitcherDescription(description);
102 setSystemChromeEnabledSystemUIOverlays(overlays);
107 setSystemChromeEnabledSystemUIMode(
mode);
111 public void setSystemUiChangeListener() {
112 setSystemChromeChangeListener();
116 public void restoreSystemUiOverlays() {
117 restoreSystemChromeSystemUIOverlays();
121 public void setSystemUiOverlayStyle(
123 setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle);
127 public void setFrameworkHandlesBack(
boolean frameworkHandlesBack) {
128 PlatformPlugin.this.setFrameworkHandlesBack(frameworkHandlesBack);
132 public void popSystemNavigator() {
137 public CharSequence getClipboardData(
143 public void setClipboardData(@NonNull String
text) {
148 public boolean clipboardHasStrings() {
153 public void share(@NonNull String
text) {
159 this(activity, platformChannel,
null);
163 @NonNull Activity activity,
166 this.activity = activity;
167 this.platformChannel = platformChannel;
169 this.platformPluginDelegate = delegate;
185 View view = activity.getWindow().getDecorView();
186 view.playSoundEffect(SoundEffectConstants.CLICK);
193 View view = activity.getWindow().getDecorView();
194 switch (feedbackType) {
196 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
199 view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
202 view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
206 view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
209 case SELECTION_CLICK:
210 view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
215 private void setSystemChromePreferredOrientations(
int androidOrientation) {
216 activity.setRequestedOrientation(androidOrientation);
219 @SuppressWarnings(
"deprecation")
220 private
void setSystemChromeApplicationSwitcherDescription(
221 PlatformChannel.AppSwitcherDescription description) {
222 if (
Build.VERSION.SDK_INT < API_LEVELS.API_28) {
223 activity.setTaskDescription(
224 new TaskDescription(description.label,
null, description.color));
226 TaskDescription taskDescription =
227 new TaskDescription(description.label, 0, description.color);
228 activity.setTaskDescription(taskDescription);
232 private void setSystemChromeChangeListener() {
234 View decorView = activity.getWindow().getDecorView();
235 decorView.setOnSystemUiVisibilityChangeListener(
236 new View.OnSystemUiVisibilityChangeListener() {
238 public void onSystemUiVisibilityChange(int visibility) {
248 if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
254 platformChannel.systemChromeChanged(true);
259 platformChannel.systemChromeChanged(false);
266 private void setSystemChromeEnabledSystemUIMode(PlatformChannel.SystemUiMode systemUiMode) {
269 if (systemUiMode == PlatformChannel.SystemUiMode.LEAN_BACK) {
278 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
279 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
280 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
281 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
282 | View.SYSTEM_UI_FLAG_FULLSCREEN;
283 }
else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE) {
292 View.SYSTEM_UI_FLAG_IMMERSIVE
293 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
294 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
295 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
296 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
297 | View.SYSTEM_UI_FLAG_FULLSCREEN;
298 }
else if (systemUiMode == PlatformChannel.SystemUiMode.IMMERSIVE_STICKY) {
306 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
307 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
308 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
309 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
310 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
311 | View.SYSTEM_UI_FLAG_FULLSCREEN;
312 }
else if (systemUiMode == PlatformChannel.SystemUiMode.EDGE_TO_EDGE
313 &&
Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
320 View.SYSTEM_UI_FLAG_LAYOUT_STABLE
321 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
322 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
328 mEnabledOverlays = enabledOverlays;
329 updateSystemUiOverlays();
332 private void setSystemChromeEnabledSystemUIOverlays(
336 int enabledOverlays =
338 | View.SYSTEM_UI_FLAG_FULLSCREEN
339 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
340 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
342 if (overlaysToShow.size() == 0) {
343 enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
347 for (
int i = 0;
i < overlaysToShow.size(); ++
i) {
348 PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(
i);
349 switch (overlayToShow) {
351 enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
353 case BOTTOM_OVERLAYS:
354 enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
355 enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
360 mEnabledOverlays = enabledOverlays;
361 updateSystemUiOverlays();
373 activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays);
374 if (currentTheme !=
null) {
375 setSystemChromeSystemUIOverlayStyle(currentTheme);
379 private void restoreSystemChromeSystemUIOverlays() {
380 updateSystemUiOverlays();
383 @SuppressWarnings(
"deprecation")
384 private
void setSystemChromeSystemUIOverlayStyle(
385 PlatformChannel.SystemChromeStyle systemChromeStyle) {
386 Window
window = activity.getWindow();
387 View view =
window.getDecorView();
388 WindowInsetsControllerCompat windowInsetsControllerCompat =
389 new WindowInsetsControllerCompat(
window, view);
391 if (
Build.VERSION.SDK_INT < API_LEVELS.API_30) {
395 window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
401 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
402 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
412 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_23) {
413 if (systemChromeStyle.statusBarIconBrightness !=
null) {
414 switch (systemChromeStyle.statusBarIconBrightness) {
418 windowInsetsControllerCompat.setAppearanceLightStatusBars(
true);
423 windowInsetsControllerCompat.setAppearanceLightStatusBars(
false);
428 if (systemChromeStyle.statusBarColor !=
null) {
429 window.setStatusBarColor(systemChromeStyle.statusBarColor);
435 if (systemChromeStyle.systemStatusBarContrastEnforced !=
null
436 &&
Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
437 window.setStatusBarContrastEnforced(systemChromeStyle.systemStatusBarContrastEnforced);
447 if (
Build.VERSION.SDK_INT >= API_LEVELS.API_26) {
448 if (systemChromeStyle.systemNavigationBarIconBrightness !=
null) {
449 switch (systemChromeStyle.systemNavigationBarIconBrightness) {
453 windowInsetsControllerCompat.setAppearanceLightNavigationBars(
true);
458 windowInsetsControllerCompat.setAppearanceLightNavigationBars(
false);
463 if (systemChromeStyle.systemNavigationBarColor !=
null) {
464 window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor);
468 if (systemChromeStyle.systemNavigationBarDividerColor !=
null
469 &&
Build.VERSION.SDK_INT >= API_LEVELS.API_28) {
470 window.setNavigationBarDividerColor(systemChromeStyle.systemNavigationBarDividerColor);
477 if (systemChromeStyle.systemNavigationBarContrastEnforced !=
null
478 &&
Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
479 window.setNavigationBarContrastEnforced(
480 systemChromeStyle.systemNavigationBarContrastEnforced);
483 currentTheme = systemChromeStyle;
486 private void setFrameworkHandlesBack(
boolean frameworkHandlesBack) {
487 if (platformPluginDelegate !=
null) {
488 platformPluginDelegate.setFrameworkHandlesBack(frameworkHandlesBack);
492 private void popSystemNavigator() {
493 if (platformPluginDelegate !=
null && platformPluginDelegate.popSystemNavigator()) {
498 if (activity instanceof OnBackPressedDispatcherOwner) {
499 ((OnBackPressedDispatcherOwner) activity).getOnBackPressedDispatcher().onBackPressed();
505 private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat
format) {
506 ClipboardManager clipboard =
507 (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
509 if (!clipboard.hasPrimaryClip())
return null;
511 CharSequence itemText =
null;
513 ClipData
clip = clipboard.getPrimaryClip();
514 if (
clip ==
null)
return null;
515 if (
format ==
null ||
format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
516 ClipData.Item item =
clip.getItemAt(0);
519 itemText = item.getText();
520 if (itemText ==
null) {
523 Uri itemUri = item.getUri();
525 if (itemUri ==
null) {
527 TAG,
"Clipboard item contained no textual content nor a URI to retrieve it from.");
532 String uriScheme = itemUri.getScheme();
534 if (!uriScheme.equals(
"content")) {
537 "Clipboard item contains a Uri with scheme '" + uriScheme +
"'that is unhandled.");
541 AssetFileDescriptor assetFileDescriptor =
542 activity.getContentResolver().openTypedAssetFileDescriptor(itemUri,
"text/*",
null);
546 itemText = item.coerceToText(activity);
547 if (assetFileDescriptor !=
null) assetFileDescriptor.close();
552 }
catch (SecurityException
e) {
555 "Attempted to get clipboard data that requires additional permission(s).\n"
556 +
"See the exception details for which permission(s) are required, and consider adding them to your Android Manifest as described in:\n"
557 +
"https://developer.android.com/guide/topics/permissions/overview",
560 }
catch (FileNotFoundException
e) {
561 Log.w(
TAG,
"Clipboard text was unable to be received from content URI.");
563 }
catch (IOException
e) {
564 Log.w(
TAG,
"Failed to close AssetFileDescriptor while trying to read text from URI.",
e);
571 private void setClipboardData(String
text) {
572 ClipboardManager clipboard =
573 (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
574 ClipData
clip = ClipData.newPlainText(
"text label?",
text);
575 clipboard.setPrimaryClip(
clip);
578 private boolean clipboardHasStrings() {
579 ClipboardManager clipboard =
580 (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
584 if (!clipboard.hasPrimaryClip()) {
587 ClipDescription description = clipboard.getPrimaryClipDescription();
588 if (description ==
null) {
591 return description.hasMimeType(
"text/*");
594 private void share(@NonNull String
text) {
595 Intent intent =
new Intent();
596 intent.setAction(Intent.ACTION_SEND);
597 intent.setType(
"text/plain");
598 intent.putExtra(Intent.EXTRA_TEXT,
text);
600 activity.startActivity(Intent.createChooser(intent,
null));
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
uint32_t uint32_t * format
void Log(const char *format,...) SK_PRINTF_LIKE(1
def Build(configs, env, options)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode