-
Couldn't load subscription status.
- Fork 46
feat: improve Vshare WebView with intent URL support and status bar padding #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
962b47b
bbc3c04
2dae224
c222c94
5907220
a54021b
daec27e
e0a7d57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,40 @@ | ||
| package me.ghui.v2er.module.vshare; | ||
|
|
||
| import android.annotation.SuppressLint; | ||
| import android.content.ActivityNotFoundException; | ||
| import android.content.Context; | ||
| import android.content.Intent; | ||
| import android.graphics.Bitmap; | ||
| import android.net.Uri; | ||
| import android.os.Bundle; | ||
| import android.util.Log; | ||
| import android.view.View; | ||
| import android.view.ViewGroup; | ||
| import android.widget.FrameLayout; | ||
| import android.webkit.WebChromeClient; | ||
| import android.webkit.WebResourceRequest; | ||
| import android.webkit.WebSettings; | ||
| import android.webkit.WebView; | ||
| import android.webkit.WebViewClient; | ||
| import android.widget.ProgressBar; | ||
| import android.widget.Toast; | ||
|
|
||
| import androidx.appcompat.app.AppCompatActivity; | ||
| import androidx.annotation.Nullable; | ||
|
|
||
| import butterknife.BindView; | ||
| import butterknife.ButterKnife; | ||
| import me.ghui.v2er.R; | ||
| import me.ghui.v2er.module.base.BaseActivity; | ||
| import me.ghui.v2er.module.base.BaseContract; | ||
| import me.ghui.v2er.util.DarkModelUtils; | ||
| import me.ghui.v2er.widget.BaseToolBar; | ||
|
|
||
| /** | ||
| * Fullscreen WebView Activity for displaying vshare page | ||
| * with automatic theme adaptation | ||
| */ | ||
| public class VshareWebActivity extends AppCompatActivity { | ||
| public class VshareWebActivity extends BaseActivity<BaseContract.IPresenter> { | ||
|
|
||
| private static final String TAG = "VshareWebActivity"; | ||
| private static final String VSHARE_BASE_URL = "https://v2er.app/vshare"; | ||
|
|
||
| @BindView(R.id.webview) | ||
|
|
@@ -40,18 +49,36 @@ public static void open(Context context) { | |
| } | ||
|
|
||
| @Override | ||
| protected void onCreate(Bundle savedInstanceState) { | ||
| super.onCreate(savedInstanceState); | ||
| protected int attachLayoutRes() { | ||
| return R.layout.activity_vshare_web; | ||
| } | ||
|
|
||
| // Hide action bar if present | ||
| if (getSupportActionBar() != null) { | ||
| getSupportActionBar().hide(); | ||
| } | ||
| @Nullable | ||
| @Override | ||
| protected BaseToolBar attachToolbar() { | ||
| // Return null to hide the toolbar for fullscreen WebView experience | ||
| return null; | ||
| } | ||
|
|
||
| // Set SystemUI flags to match MainActivity's edge-to-edge behavior | ||
| @Override | ||
| protected void reloadMode(int mode) { | ||
| // Recreate activity to apply new theme | ||
| recreate(); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean supportSlideBack() { | ||
| // Disable slide back for fullscreen WebView | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| protected void init() { | ||
| super.init(); | ||
|
|
||
| // Apply fullscreen flags for edge-to-edge WebView | ||
| View decorView = getWindow().getDecorView(); | ||
| int systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | ||
| | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | ||
| int systemUiVisibility = decorView.getSystemUiVisibility() | ||
| | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; | ||
|
|
||
| // Set status bar icon color based on theme | ||
|
|
@@ -64,8 +91,8 @@ protected void onCreate(Bundle savedInstanceState) { | |
|
|
||
| decorView.setSystemUiVisibility(systemUiVisibility); | ||
|
|
||
| setContentView(R.layout.activity_vshare_web); | ||
| ButterKnife.bind(this); | ||
| // Set WebView top margin to status bar height | ||
| applyStatusBarMargin(); | ||
|
|
||
| setupWebView(); | ||
|
|
||
|
|
@@ -114,8 +141,12 @@ private void setupWebView() { | |
| mWebView.setWebViewClient(new WebViewClient() { | ||
| @Override | ||
| public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { | ||
| // Let the WebView handle the navigation | ||
| return false; | ||
| return handleUrlLoading(request.getUrl().toString()); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean shouldOverrideUrlLoading(WebView view, String url) { | ||
| return handleUrlLoading(url); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -149,6 +180,114 @@ public void onProgressChanged(WebView view, int newProgress) { | |
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Apply status bar height as top margin to WebView | ||
| */ | ||
| private void applyStatusBarMargin() { | ||
| int statusBarHeight = getStatusBarHeight(); | ||
| if (statusBarHeight > 0) { | ||
| ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mWebView.getLayoutParams(); | ||
| if (params instanceof FrameLayout.LayoutParams) { | ||
| params.topMargin = statusBarHeight; | ||
| mWebView.setLayoutParams(params); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get status bar height dynamically | ||
| */ | ||
| private int getStatusBarHeight() { | ||
| int result = 0; | ||
| int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); | ||
| if (resourceId > 0) { | ||
| result = getResources().getDimensionPixelSize(resourceId); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Handle URL loading for WebView | ||
| * Returns true if the URL was handled externally, false if WebView should load it | ||
| */ | ||
| private boolean handleUrlLoading(String url) { | ||
| if (url == null) { | ||
| return false; | ||
| } | ||
|
|
||
| Uri uri = Uri.parse(url); | ||
| String scheme = uri.getScheme(); | ||
|
|
||
| // Handle intent:// URLs (e.g., Google Play Store links) | ||
| if ("intent".equals(scheme)) { | ||
| try { | ||
| Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); | ||
|
|
||
| // Check if there's an app that can handle this intent | ||
| if (getPackageManager().resolveActivity(intent, 0) != null) { | ||
| startActivity(intent); | ||
| return true; | ||
| } | ||
|
|
||
| // Fallback: Try to open the browser_fallback_url if available | ||
| String fallbackUrl = intent.getStringExtra("browser_fallback_url"); | ||
| if (fallbackUrl != null) { | ||
| mWebView.loadUrl(fallbackUrl); | ||
| return true; | ||
| } | ||
|
|
||
| // Last resort: Try to open in Google Play if it's a Play Store intent | ||
| String packageName = intent.getPackage(); | ||
| if (packageName != null) { | ||
| Intent marketIntent = new Intent(Intent.ACTION_VIEW, | ||
| Uri.parse("market://details?id=" + packageName)); | ||
| marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
| if (getPackageManager().resolveActivity(marketIntent, 0) != null) { | ||
|
||
| startActivity(marketIntent); | ||
| return true; | ||
| } | ||
| } | ||
| } catch (Exception e) { | ||
| Log.e(TAG, "Error handling intent URL: " + url, e); | ||
| Toast.makeText(this, "Unable to open app", Toast.LENGTH_SHORT).show(); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| // Handle market:// URLs (Google Play Store) | ||
| if ("market".equals(scheme)) { | ||
| try { | ||
| Intent intent = new Intent(Intent.ACTION_VIEW, uri); | ||
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
| startActivity(intent); | ||
| return true; | ||
| } catch (ActivityNotFoundException e) { | ||
| Log.e(TAG, "Google Play Store not found", e); | ||
| // Fallback to web version | ||
| String webUrl = url.replace("market://", "https://play.google.com/store/apps/"); | ||
| mWebView.loadUrl(webUrl); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| // Handle other app-specific schemes (e.g., mailto:, tel:, etc.) | ||
| if (!"http".equals(scheme) && !"https".equals(scheme)) { | ||
| try { | ||
| Intent intent = new Intent(Intent.ACTION_VIEW, uri); | ||
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
| startActivity(intent); | ||
| return true; | ||
| } catch (ActivityNotFoundException e) { | ||
| Log.e(TAG, "No app found to handle scheme: " + scheme, e); | ||
| Toast.makeText(this, "No app found to open this link", Toast.LENGTH_SHORT).show(); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| // Let WebView handle normal http/https URLs | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public void onBackPressed() { | ||
| // Handle back navigation in WebView | ||
|
|
@@ -170,4 +309,4 @@ protected void onDestroy() { | |
| } | ||
| super.onDestroy(); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resolveActivity() call with flags=0 is deprecated in API level 30+. Use PackageManager.ResolveInfo flags or PackageManager.MATCH_DEFAULT_ONLY for better compatibility.