geolokasi tampilan web android

103

Saya harus mengambil lokasi pengguna di file WebView. Saya melakukan ini dengan Javascript berikut:

function getLocation() {
   navigator.geolocation.getCurrentPosition(displayLocation, handleError);
}

Tetapi popup permintaan izin tidak pernah terbuka.

Saya telah mengatur pengaturan ini:

ws.setJavaScriptEnabled(true);
ws.setGeolocationEnabled(true);
ws.setJavaScriptCanOpenWindowsAutomatically(true);

Apa cara yang benar untuk mengakses lokasi pengguna dari dalam WebView?

Ste
sumber

Jawaban:

221
  • JavaScript harus diaktifkan di WebView, menggunakanWebSettings.setJavaScriptEnabled(true);
  • Aplikasi membutuhkan izin ACCESS_FINE_LOCATION
  • The WebViewharus menggunakan kustom WebChromeClientyang mengimplementasikan WebChromeClient.onGeolocationPermissionsShowPrompt(). Metode ini dipanggil oleh WebViewuntuk mendapatkan izin untuk mengungkapkan lokasi pengguna ke JavaScript. (Dalam kasus browser, kami menampilkan prompt kepada pengguna.) Implementasi default tidak melakukan apa-apa, jadi izin tidak pernah diperoleh dan lokasi tidak pernah diteruskan ke JavaScript. Implementasi sederhana yang selalu memberikan izin adalah ...

    webView.setWebChromeClient(new WebChromeClient() {
     public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
        callback.invoke(origin, true, false);
     }
    });

Geolokasi menggunakan database untuk mempertahankan posisi cache dan izin antar sesi. Lokasi database diatur menggunakan WebSettings.setGeolocationDatabasePath(...). Jika lokasi database tidak disetel, penyimpanan persisten tidak akan tersedia, tetapi Geolokasi akan terus berfungsi dengan benar jika tidak. Untuk mengatur lokasi database, gunakan ...

webView.getSettings().setGeolocationDatabasePath( context.getFilesDir().getPath() );
Chris Cashwell
sumber
6
jawaban ini benar, tetapi Anda harus menggunakan android.webkit.GeolocationPermissions.Callback
NullPointerException
3
Berikut adalah contoh kode lengkap turbomanage.wordpress.com/2012/04/23/…
giorgio79
2
di mana "konteks" didefinisikan?
Grafik C
5
Saya akhirnya harus menggunakan yang sepenuhnya memenuhi syarat android.webkit.WebChromeClient()alih-alih hanya WebChromeClient()dan android.webkit.GeolocationPermissions.Callbackuntuk membuat ini berfungsi.
Biksu Gendut
2
Ini tidak berfungsi lagi dengan target / runtime> Android 6.0, karena izin runtime, bukan?
最 白 目
17

Apakah Anda menyatakan izin itu dalam manifes Anda?

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Anda mungkin juga perlu menyatakan izin lokasi lain, seperti ini:

<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Chris Cashwell
sumber
3
Ya saya menyatakan izin ini. Tampilan web mengambil lokasi pengguna, tetapi masalahnya adalah dialog untuk menerima atau menolak lokasi pengguna tidak muncul.
Ste
3
Hanya yang pertama yang dibutuhkan!
TacB0sS
17

Dialog untuk menerima atau menolak lokasi pengguna dirancang oleh programmer: D. Seperti yang dikatakan Chris Cashwell, Anda cukup menggunakan panggilan balik seperti ini:

webview.setWebChromeClient(new WebChromeClient(){
  public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
    // callback.invoke(String origin, boolean allow, boolean remember);
    callback.invoke(origin, true, false);
  }
}

Dalam beberapa kasus, HTML5 perlu menggunakan penyimpanan, Anda harus mengaktifkan beberapa properti agar tampilan web memiliki akses penuh untuk berjalan normal.

    // HTML5 API flags
    webView.getSettings().setAppCacheEnabled(true);
    webView.getSettings().setDatabaseEnabled(true);
    webView.getSettings().setDomStorageEnabled(true);
gempa bumi
sumber
15

Berbagi kelas aktivitas Kerja saya, ini adalah solusi lengkap yang dapat menunjukkan

  • Menampilkan dialog pemuatan saat halaman web sedang dimuat
  • Minta izin di marshmallow dan di atasnya
  • Tangani kesalahan halaman web
  • Periksa koneksi internet dan buka halaman pengaturan
  • Menangani izin Geolokasi dengan dan tanpa dialog

Semoga menghemat waktu seseorang

    /**
     * Created by Hitesh.Sahu on 3/24/2017.
     */

    public class WebViewActivity extends AppCompatActivity {

        final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
        private int webViewPreviousState;
        private final int PAGE_STARTED = 0x1;
        private final int PAGE_REDIRECTED = 0x2;
        private CoordinatorLayout rootView;
        private WebView webView;


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_webview);
            webView = (WebView) findViewById(R.id.webView);
            rootView = (CoordinatorLayout) findViewById(R.id.root_view);

            if (Build.VERSION.SDK_INT >= 23) {
                // Marshmallow+ Permission APIs
                fuckMarshMallow();
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
                    WebView.setWebContentsDebuggingEnabled(true);
                }
            }
            webView.setInitialScale(1);
            webView.getSettings().setLoadWithOverviewMode(true);
            webView.getSettings().setUseWideViewPort(true);
            webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
            webView.setScrollbarFadingEnabled(false);

            webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
            webView.getSettings().setBuiltInZoomControls(true);
            webView.setWebViewClient(new GeoWebViewClient());
            // Below required for geolocation
            webView.getSettings().setJavaScriptEnabled(true);
            webView.getSettings().setGeolocationEnabled(true);
            webView.setWebChromeClient(new GeoWebChromeClient());

            webView.getSettings().setAppCacheEnabled(true);
            webView.getSettings().setDatabaseEnabled(true);
            webView.getSettings().setDomStorageEnabled(true);

            webView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());

            webView.loadUrl("file:///android_asset/index.html");
        }

        /**
         * WebChromeClient subclass handles UI-related calls
         * Note: think chrome as in decoration, not the Chrome browser
         */
        public class GeoWebChromeClient extends android.webkit.WebChromeClient {
            @Override
            public void onGeolocationPermissionsShowPrompt(final String origin,
                                                           final GeolocationPermissions.Callback callback) {
                // Always grant permission since the app itself requires location
                // permission and the user has therefore already granted it
                callback.invoke(origin, true, false);

    //            final boolean remember = false;
    //            AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);
    //            builder.setTitle("Locations");
    //            builder.setMessage("Would like to use your Current Location ")
    //                    .setCancelable(true).setPositiveButton("Allow", new DialogInterface.OnClickListener() {
    //                public void onClick(DialogInterface dialog, int id) {
    //                    // origin, allow, remember
    //                    callback.invoke(origin, true, remember);
    //                }
    //            }).setNegativeButton("Don't Allow", new DialogInterface.OnClickListener() {
    //                public void onClick(DialogInterface dialog, int id) {
    //                    // origin, allow, remember
    //                    callback.invoke(origin, false, remember);
    //                }
    //            });
    //            AlertDialog alert = builder.create();
    //            alert.show();
            }
        }

        /**
         * WebViewClient subclass loads all hyperlinks in the existing WebView
         */
        public class GeoWebViewClient extends WebViewClient {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // When user clicks a hyperlink, load in the existing WebView
                view.loadUrl(url);
                return true;
            }

            Dialog loadingDialog = new Dialog(WebViewActivity.this);

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                webViewPreviousState = PAGE_STARTED;

                if (loadingDialog == null || !loadingDialog.isShowing())
                    loadingDialog = ProgressDialog.show(WebViewActivity.this, "",
                            "Loading Please Wait", true, true,
                            new DialogInterface.OnCancelListener() {

                                @Override
                                public void onCancel(DialogInterface dialog) {
                                    // do something
                                }
                            });

                loadingDialog.setCancelable(false);
            }


            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onReceivedError(WebView view, WebResourceRequest request,
                                        WebResourceError error) {


                if (isConnected()) {
                    final Snackbar snackBar = Snackbar.make(rootView, "onReceivedError : " + error.getDescription(), Snackbar.LENGTH_INDEFINITE);
                    snackBar.setAction("Reload", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            webView.loadUrl("javascript:window.location.reload( true )");
                        }
                    });
                    snackBar.show();
                } else {
                    final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
                    snackBar.setAction("Enable Data", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
                            webView.loadUrl("javascript:window.location.reload( true )");
                            snackBar.dismiss();
                        }
                    });
                    snackBar.show();
                }

                super.onReceivedError(view, request, error);

            }

            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onReceivedHttpError(WebView view,
                                            WebResourceRequest request, WebResourceResponse errorResponse) {

                if (isConnected()) {
                    final Snackbar snackBar = Snackbar.make(rootView, "HttpError : " + errorResponse.getReasonPhrase(), Snackbar.LENGTH_INDEFINITE);

                    snackBar.setAction("Reload", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            webView.loadUrl("javascript:window.location.reload( true )");
                        }
                    });
                    snackBar.show();
                } else {
                    final Snackbar snackBar = Snackbar.make(rootView, "No Internet Connection ", Snackbar.LENGTH_INDEFINITE);
                    snackBar.setAction("Enable Data", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            startActivityForResult(new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
                            webView.loadUrl("javascript:window.location.reload( true )");
                            snackBar.dismiss();
                        }
                    });
                    snackBar.show();
                }
                super.onReceivedHttpError(view, request, errorResponse);
            }

            @Override
            public void onPageFinished(WebView view, String url) {

                if (webViewPreviousState == PAGE_STARTED) {

                    if (null != loadingDialog) {
                        loadingDialog.dismiss();
                        loadingDialog = null;
                    }
                }
            }
        }


        /**
         * Check if there is any connectivity
         *
         * @return is Device Connected
         */
        public boolean isConnected() {

            ConnectivityManager cm = (ConnectivityManager)
                    this.getSystemService(Context.CONNECTIVITY_SERVICE);

            if (null != cm) {
                NetworkInfo info = cm.getActiveNetworkInfo();
                return (info != null && info.isConnected());
            }

            return false;

        }

        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            switch (requestCode) {
                case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
                    Map<String, Integer> perms = new HashMap<String, Integer>();
                    // Initial
                    perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);


                    // Fill with results
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);

                    // Check for ACCESS_FINE_LOCATION
                    if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED


                            ) {
                        // All Permissions Granted

                        // Permission Denied
                        Toast.makeText(WebViewActivity.this, "All Permission GRANTED !! Thank You :)", Toast.LENGTH_SHORT)
                                .show();

                    } else {
                        // Permission Denied
                        Toast.makeText(WebViewActivity.this, "One or More Permissions are DENIED Exiting App :(", Toast.LENGTH_SHORT)
                                .show();

                        finish();
                    }
                }
                break;
                default:
                    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }

        @TargetApi(Build.VERSION_CODES.M)
        private void fuckMarshMallow() {
            List<String> permissionsNeeded = new ArrayList<String>();

            final List<String> permissionsList = new ArrayList<String>();
            if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
                permissionsNeeded.add("Show Location");

            if (permissionsList.size() > 0) {
                if (permissionsNeeded.size() > 0) {

                    // Need Rationale
                    String message = "App need access to " + permissionsNeeded.get(0);

                    for (int i = 1; i < permissionsNeeded.size(); i++)
                        message = message + ", " + permissionsNeeded.get(i);

                    showMessageOKCancel(message,
                            new DialogInterface.OnClickListener() {

                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                            REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                                }
                            });
                    return;
                }
                requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                return;
            }

            Toast.makeText(WebViewActivity.this, "No new Permission Required- Launching App .You are Awesome!!", Toast.LENGTH_SHORT)
                    .show();
        }


        private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
            new AlertDialog.Builder(WebViewActivity.this)
                    .setMessage(message)
                    .setPositiveButton("OK", okListener)
                    .setNegativeButton("Cancel", null)
                    .create()
                    .show();
        }

        @TargetApi(Build.VERSION_CODES.M)
        private boolean addPermission(List<String> permissionsList, String permission) {

            if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                permissionsList.add(permission);
                // Check for Rationale Option
                if (!shouldShowRequestPermissionRationale(permission))
                    return false;
            }
            return true;
        }
    }
Hitesh Sahu
sumber
2
Ini adalah satu-satunya jawaban yang mempertimbangkan fakta bahwa saat membangun ke Android versi 6+ Anda perlu meminta izin selama waktu proses.
hatzaviv
1
LMAO, F Marshmallow. Ha ha ha.
Taslim Oseni
5

Ini adalah contoh menampilkan dialog peringatan untuk mempromosikan izin pengguna untuk menggunakan lokasinya:

     @Override
     public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
        Log.i(TAG, "onGeolocationPermissionsShowPrompt()");

        final boolean remember = false;
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Locations");
        builder.setMessage("Would like to use your Current Location ")
        .setCancelable(true).setPositiveButton("Allow", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
              // origin, allow, remember
              callback.invoke(origin, true, remember);
           }
        }).setNegativeButton("Don't Allow", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
              // origin, allow, remember
              callback.invoke(origin, false, remember);
           }
        });
        AlertDialog alert = builder.create();
        alert.show();
     }
Gary S.
sumber
4
Terima kasih! Satu perubahan kecil: Argumen yang diteruskan ke konstruktor AlertDialog.Builder harus menjadi aktivitas Anda. Dalam konteks itu, "ini" salah.
Joseph Lennox
bisa ada orang yang memberi saya kode contoh yang berfungsi penuh untuk ini. Terima kasih sebelumnya.
shams
2
saya telah melakukan semua ini tetapi kodenya masih belum menyala apa yang salah? Saya mengujinya di emulator gerhana
palsu
1

Saya baru-baru ini menemukan jenis situasi ini dan telah melakukan langkah-langkah di bawah ini untuk mencapai ini:

Langkah 1: Tambahkan izin di AndroidManifest.xmlfile Anda

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 

Langkah 2: Buat aktivitas yang akan berisi WebViewdan ProgressBar(dalam kasus saya)

xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="0dp"
        android:minHeight="4dp"
        android:padding="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <WebView
        android:id="@+id/webView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />
</androidx.constraintlayout.widget.ConstraintLayout>

Langkah 3: Di kelas aktivitas Anda, tambahkan kode di bawah ini dan buat perubahan sesuai kebutuhan Anda

class WebActivity : AppCompatActivity() {
    var pageUrl: String = "https://couponia.co/"
    var mGeoLocationRequestOrigin: String? = null
    var mGeoLocationCallback: GeolocationPermissions.Callback? = null
    val MAX_PROGRESS = 100
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_web)
        initWebView()
        setWebClient()
        loadUrl(pageUrl)
    }


    @SuppressLint("SetJavaScriptEnabled")
    private fun initWebView() {
        webView.settings.javaScriptEnabled = true
        webView.settings.loadWithOverviewMode = true
        webView.settings.useWideViewPort = true
        webView.settings.domStorageEnabled = true
        webView.settings.databaseEnabled = true
        webView.settings.setAppCacheEnabled(true)
        webView.webViewClient = object : WebViewClient() {
            override
            fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                handler?.proceed()
            }
        }

    }

    private fun setWebClient() {

        webView.webChromeClient = object : WebChromeClient() {
            override fun onGeolocationPermissionsShowPrompt(
                origin: String?,
                callback: GeolocationPermissions.Callback?
            ) {

                if (ContextCompat.checkSelfPermission(
                        this@WebActivity,
                        Manifest.permission.ACCESS_FINE_LOCATION
                    )
                    != PackageManager.PERMISSION_GRANTED
                ) {

                    if (ActivityCompat.shouldShowRequestPermissionRationale(
                            this@WebActivity,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    ) {
                        AlertDialog.Builder(this@WebActivity)
                            .setMessage("Please turn ON the GPS to make app work smoothly")
                            .setNeutralButton(
                                android.R.string.ok,
                                DialogInterface.OnClickListener { dialogInterface, i ->
                                    mGeoLocationCallback = callback
                                    mGeoLocationRequestOrigin = origin
                                    ActivityCompat.requestPermissions(
                                        this@WebActivity,
                                        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1001
                                    )

                                })
                            .show()

                    } else {
                        //no explanation need we can request the locatio
                        mGeoLocationCallback = callback
                        mGeoLocationRequestOrigin = origin
                        ActivityCompat.requestPermissions(
                            this@WebActivity,
                            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1001
                        )
                    }
                } else {
                    //tell the webview that permission has granted
                    callback!!.invoke(origin, true, true)
                }

            }

            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                super.onProgressChanged(view, newProgress)
                progressBar.progress = newProgress
                if (newProgress < MAX_PROGRESS && progressBar.visibility == ProgressBar.GONE) {
                    progressBar.visibility = ProgressBar.VISIBLE
                }
                if (newProgress == MAX_PROGRESS) {
                    progressBar.visibility = ProgressBar.GONE
                }
            }


        }
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        // Check if the key event was the Back button and if there's history
        if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
            webView.goBack()
            return true
        }
        // If it wasn't the Back key or there's no web page history, exit the activity)
        return super.onKeyDown(keyCode, event)
    }

    private fun loadUrl(pageUrl: String) {
        webView.loadUrl(pageUrl)
    }


    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        when (requestCode) {
            1001 -> {
                //if permission is cancel result array would be empty
                if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //permission was granted
                    if (mGeoLocationCallback != null) {
                        mGeoLocationCallback!!.invoke(mGeoLocationRequestOrigin, true, true)
                    }
                } else {
                    //permission denied
                    if (mGeoLocationCallback != null) {
                        mGeoLocationCallback!!.invoke(mGeoLocationRequestOrigin, false, false)
                    }
                }
            }

        }
    }
}
Suraj Bahadur
sumber