mirror of
https://github.com/zxing/zxing.git
synced 2024-11-10 04:54:04 -08:00
Final changes for v2.7 of Barcode Scanner, including sending product lookups to the mobile version of Google Product Search.
git-svn-id: https://zxing.googlecode.com/svn/trunk@923 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
0de1fbc623
commit
ae1241880b
|
@ -20,8 +20,9 @@ version to be published. The next versionCode will be 7, regardless of whether t
|
|||
versionName is 2.31, 2.4, or 3.0. -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.zxing.client.android"
|
||||
android:versionName="2.6"
|
||||
android:versionCode="13">
|
||||
android:versionName="2.7"
|
||||
android:versionCode="18">
|
||||
<uses-sdk android:minSdkVersion="1"/>
|
||||
<application android:icon="@drawable/launcher_icon"
|
||||
android:label="@string/app_name">
|
||||
<activity android:name=".CaptureActivity"
|
||||
|
@ -48,6 +49,20 @@ versionName is 2.31, 2.4, or 3.0. -->
|
|||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan" />
|
||||
</intent-filter>
|
||||
<!-- We also support a Google Product Search URL. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan" />
|
||||
</intent-filter>
|
||||
<!-- And the UK version. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="PreferencesActivity"
|
||||
android:label="@string/preferences_name">
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<body>
|
||||
<link rel="StyleSheet" href="style.css" type="text/css">
|
||||
<h3><center>What's new in this version</center></h3>
|
||||
<p>New in version 2.6:</p>
|
||||
<p>New in version 2.7:</p>
|
||||
<ul>
|
||||
<li>Added support for ITF format barcodes.</li>
|
||||
<li>Fixed a bug where some URLs would be misinterpreted as email addresses.</li>
|
||||
<li>Added support for uppercase URIs like TEL: and GEO:.</li>
|
||||
<li>Prevented Barcode Scanner from crashing when launching an unhandled intent.</li>
|
||||
<li>Web apps can now launch Barcode Scanner by creating a hyperlink to <i>http://zxing.appspot.com/scan</i>.</li>
|
||||
<li>Added the version to the about box.</li>
|
||||
<li>Searching for a product online now uses the new mobile version of Google Product Search, which
|
||||
is formatted for small screens and loads quicker.</li>
|
||||
<li>Two fixes in QR Code version detection.</li>
|
||||
<li>Fixed encoding QR Codes of geo locations.</li>
|
||||
<li>You can now encode a contact as a QR Code even if it doesn't have a name, i.e. just a phone
|
||||
number.</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -22,6 +22,7 @@
|
|||
<item type="id" name="decode_succeeded"/>
|
||||
<item type="id" name="encode_failed"/>
|
||||
<item type="id" name="encode_succeeded"/>
|
||||
<item type="id" name="launch_product_query"/>
|
||||
<item type="id" name="quit"/>
|
||||
<item type="id" name="restart_preview"/>
|
||||
<item type="id" name="return_scan_result"/>
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
<string name="msg_share_subject_line">Here\'s the contents of a barcode I scanned</string>
|
||||
|
||||
<string name="preferences_actions_title">When a barcode is found\u2026</string>
|
||||
<string name="preferences_copy_to_clipboard_title">Copy contents to clipboard</string>
|
||||
<string name="preferences_copy_to_clipboard_title">Copy to clipboard</string>
|
||||
<string name="preferences_decode_1D_title">Decode 1D barcodes</string>
|
||||
<string name="preferences_decode_QR_title">Decode QR Codes</string>
|
||||
<string name="preferences_general_title">General settings</string>
|
||||
|
|
|
@ -37,9 +37,10 @@ import android.os.Bundle;
|
|||
import android.os.Message;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.ClipboardManager;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
|
@ -53,7 +54,6 @@ import android.view.WindowManager;
|
|||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.util.Log;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.client.android.result.ResultButtonListener;
|
||||
|
@ -81,6 +81,16 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
private static final long VIBRATE_DURATION = 200;
|
||||
|
||||
private static final String PACKAGE_NAME = "com.google.zxing.client.android";
|
||||
private static final String PRODUCT_SEARCH_URL_PREFIX = "http://www.google";
|
||||
private static final String PRODUCT_SEARCH_URL_SUFFIX = "/m/products/scan";
|
||||
private static final String ZXING_URL = "http://zxing.appspot.com/scan";
|
||||
|
||||
private enum Source {
|
||||
NATIVE_APP_INTENT,
|
||||
PRODUCT_SEARCH_LINK,
|
||||
ZXING_LINK,
|
||||
NONE
|
||||
}
|
||||
|
||||
public CaptureActivityHandler mHandler;
|
||||
|
||||
|
@ -93,9 +103,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
private boolean mPlayBeep;
|
||||
private boolean mVibrate;
|
||||
private boolean mCopyToClipboard;
|
||||
private boolean mScanIntent;
|
||||
private Source mSource;
|
||||
private String mSourceUrl;
|
||||
private String mDecodeMode;
|
||||
private String versionName;
|
||||
private String mVersionName;
|
||||
|
||||
private final OnCompletionListener mBeepListener = new BeepListener();
|
||||
|
||||
|
@ -137,13 +148,35 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
if (intent != null && action != null && (action.equals(Intents.Scan.ACTION) ||
|
||||
action.equals(Intents.Scan.DEPRECATED_ACTION))) {
|
||||
mScanIntent = true;
|
||||
mDecodeMode = intent.getStringExtra(Intents.Scan.MODE);
|
||||
resetStatusView();
|
||||
String dataString = intent.getDataString();
|
||||
if (intent != null && action != null) {
|
||||
if (action.equals(Intents.Scan.ACTION) || action.equals(Intents.Scan.DEPRECATED_ACTION)) {
|
||||
// Scan the formats the intent requested, and return the result to the calling activity.
|
||||
mSource = Source.NATIVE_APP_INTENT;
|
||||
mDecodeMode = intent.getStringExtra(Intents.Scan.MODE);
|
||||
resetStatusView();
|
||||
} else if (dataString != null && dataString.contains(PRODUCT_SEARCH_URL_PREFIX) &&
|
||||
dataString.contains(PRODUCT_SEARCH_URL_PREFIX)) {
|
||||
// Scan only products and send the result to mobile Product Search.
|
||||
mSource = Source.PRODUCT_SEARCH_LINK;
|
||||
mSourceUrl = dataString;
|
||||
mDecodeMode = Intents.Scan.PRODUCT_MODE;
|
||||
resetStatusView();
|
||||
} else if (dataString != null && dataString.equals(ZXING_URL)) {
|
||||
// Scan all formats and handle the results ourselves.
|
||||
// TODO: In the future we could allow the hyperlink to include a URL to send the results to.
|
||||
mSource = Source.ZXING_LINK;
|
||||
mSourceUrl = dataString;
|
||||
mDecodeMode = null;
|
||||
resetStatusView();
|
||||
} else {
|
||||
// Scan all formats and handle the results ourselves (launched from Home).
|
||||
mSource = Source.NONE;
|
||||
mDecodeMode = null;
|
||||
resetStatusView();
|
||||
}
|
||||
} else {
|
||||
mScanIntent = false;
|
||||
mSource = Source.NONE;
|
||||
mDecodeMode = null;
|
||||
if (mLastResult == null) {
|
||||
resetStatusView();
|
||||
|
@ -170,11 +203,11 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (mScanIntent) {
|
||||
if (mSource == Source.NATIVE_APP_INTENT) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return true;
|
||||
} else if (mLastResult != null) {
|
||||
} else if ((mSource == Source.NONE || mSource == Source.ZXING_LINK) && mLastResult != null) {
|
||||
resetStatusView();
|
||||
mHandler.sendEmptyMessage(R.id.restart_preview);
|
||||
return true;
|
||||
|
@ -230,7 +263,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
}
|
||||
case ABOUT_ID:
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getString(R.string.title_about) + versionName);
|
||||
builder.setTitle(getString(R.string.title_about) + mVersionName);
|
||||
builder.setMessage(getString(R.string.msg_about) + "\n\n" + getString(R.string.zxing_url));
|
||||
builder.setIcon(R.drawable.zxing_icon);
|
||||
builder.setPositiveButton(R.string.button_open_browser, mAboutListener);
|
||||
|
@ -274,60 +307,21 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
*
|
||||
* @param rawResult The contents of the barcode.
|
||||
* @param barcode A greyscale bitmap of the camera data which was decoded.
|
||||
* @param duration How long the decoding took in milliseconds.
|
||||
*/
|
||||
public void handleDecode(Result rawResult, Bitmap barcode, int duration) {
|
||||
public void handleDecode(Result rawResult, Bitmap barcode) {
|
||||
mLastResult = rawResult;
|
||||
playBeepSoundAndVibrate();
|
||||
drawResultPoints(barcode, rawResult);
|
||||
|
||||
if (mScanIntent) {
|
||||
handleDecodeForScanIntent(rawResult, barcode, duration);
|
||||
} else {
|
||||
mStatusView.setVisibility(View.GONE);
|
||||
mViewfinderView.setVisibility(View.GONE);
|
||||
mResultView.setVisibility(View.VISIBLE);
|
||||
|
||||
ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
|
||||
barcodeImageView.setMaxWidth(MAX_RESULT_IMAGE_SIZE);
|
||||
barcodeImageView.setMaxHeight(MAX_RESULT_IMAGE_SIZE);
|
||||
barcodeImageView.setImageBitmap(barcode);
|
||||
|
||||
TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
|
||||
formatTextView.setText(getString(R.string.msg_default_format) + ": " +
|
||||
rawResult.getBarcodeFormat().toString());
|
||||
|
||||
ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
|
||||
TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
|
||||
typeTextView.setText(getString(R.string.msg_default_type) + ": " +
|
||||
resultHandler.getType().toString());
|
||||
|
||||
TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
|
||||
CharSequence title = getString(resultHandler.getDisplayTitle());
|
||||
SpannableStringBuilder styled = new SpannableStringBuilder(title + "\n\n");
|
||||
styled.setSpan(new UnderlineSpan(), 0, title.length(), 0);
|
||||
CharSequence displayContents = resultHandler.getDisplayContents();
|
||||
styled.append(displayContents);
|
||||
contentsTextView.setText(styled);
|
||||
|
||||
int buttonCount = resultHandler.getButtonCount();
|
||||
ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
|
||||
buttonView.requestFocus();
|
||||
for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
|
||||
Button button = (Button) buttonView.getChildAt(x);
|
||||
if (x < buttonCount) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
button.setText(resultHandler.getButtonText(x));
|
||||
button.setOnClickListener(new ResultButtonListener(resultHandler, x));
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCopyToClipboard) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
clipboard.setText(displayContents);
|
||||
}
|
||||
switch (mSource) {
|
||||
case NATIVE_APP_INTENT:
|
||||
case PRODUCT_SEARCH_LINK:
|
||||
handleDecodeExternally(rawResult, barcode);
|
||||
break;
|
||||
case ZXING_LINK:
|
||||
case NONE:
|
||||
handleDecodeInternally(rawResult, barcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +356,56 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
}
|
||||
}
|
||||
|
||||
private void handleDecodeForScanIntent(Result rawResult, Bitmap barcode, int duration) {
|
||||
// Put up our own UI for how to handle the decoded contents.
|
||||
private void handleDecodeInternally(Result rawResult, Bitmap barcode) {
|
||||
mStatusView.setVisibility(View.GONE);
|
||||
mViewfinderView.setVisibility(View.GONE);
|
||||
mResultView.setVisibility(View.VISIBLE);
|
||||
|
||||
ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
|
||||
barcodeImageView.setMaxWidth(MAX_RESULT_IMAGE_SIZE);
|
||||
barcodeImageView.setMaxHeight(MAX_RESULT_IMAGE_SIZE);
|
||||
barcodeImageView.setImageBitmap(barcode);
|
||||
|
||||
TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
|
||||
formatTextView.setText(getString(R.string.msg_default_format) + ": " +
|
||||
rawResult.getBarcodeFormat().toString());
|
||||
|
||||
ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
|
||||
TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
|
||||
typeTextView.setText(getString(R.string.msg_default_type) + ": " +
|
||||
resultHandler.getType().toString());
|
||||
|
||||
TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
|
||||
CharSequence title = getString(resultHandler.getDisplayTitle());
|
||||
SpannableStringBuilder styled = new SpannableStringBuilder(title + "\n\n");
|
||||
styled.setSpan(new UnderlineSpan(), 0, title.length(), 0);
|
||||
CharSequence displayContents = resultHandler.getDisplayContents();
|
||||
styled.append(displayContents);
|
||||
contentsTextView.setText(styled);
|
||||
|
||||
int buttonCount = resultHandler.getButtonCount();
|
||||
ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
|
||||
buttonView.requestFocus();
|
||||
for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
|
||||
Button button = (Button) buttonView.getChildAt(x);
|
||||
if (x < buttonCount) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
button.setText(resultHandler.getButtonText(x));
|
||||
button.setOnClickListener(new ResultButtonListener(resultHandler, x));
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCopyToClipboard) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
clipboard.setText(displayContents);
|
||||
}
|
||||
}
|
||||
|
||||
// Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
|
||||
private void handleDecodeExternally(Result rawResult, Bitmap barcode) {
|
||||
mViewfinderView.drawResultBitmap(barcode);
|
||||
|
||||
// Since this message will only be shown for a second, just tell the user what kind of
|
||||
|
@ -381,14 +424,24 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
clipboard.setText(resultHandler.getDisplayContents());
|
||||
}
|
||||
|
||||
// Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
|
||||
// the deprecated intent is retired.
|
||||
Intent intent = new Intent(getIntent().getAction());
|
||||
intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
|
||||
intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
|
||||
Message message = Message.obtain(mHandler, R.id.return_scan_result);
|
||||
message.obj = intent;
|
||||
mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
|
||||
if (mSource == Source.NATIVE_APP_INTENT) {
|
||||
// Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
|
||||
// the deprecated intent is retired.
|
||||
Intent intent = new Intent(getIntent().getAction());
|
||||
intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
|
||||
intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
|
||||
Message message = Message.obtain(mHandler, R.id.return_scan_result);
|
||||
message.obj = intent;
|
||||
mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
|
||||
} else if (mSource == Source.PRODUCT_SEARCH_LINK) {
|
||||
// Reformulate the URL which triggered us into a query, so that the request goes to the same
|
||||
// TLD as the scan URL.
|
||||
Message message = Message.obtain(mHandler, R.id.launch_product_query);
|
||||
int end = mSourceUrl.lastIndexOf("/scan");
|
||||
message.obj = mSourceUrl.substring(0, end) + "?q=" +
|
||||
resultHandler.getDisplayContents().toString() + "&source=zxing";
|
||||
mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -402,7 +455,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
|
|||
int currentVersion = info.versionCode;
|
||||
// Since we're paying to talk to the PackageManager anyway, it makes sense to cache the app
|
||||
// version name here for display in the about box later.
|
||||
this.versionName = info.versionName;
|
||||
this.mVersionName = info.versionName;
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
int lastVersion = prefs.getInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, 0);
|
||||
if (currentVersion > lastVersion) {
|
||||
|
|
|
@ -19,6 +19,7 @@ package com.google.zxing.client.android;
|
|||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
@ -70,8 +71,7 @@ public final class CaptureActivityHandler extends Handler {
|
|||
mState = State.SUCCESS;
|
||||
Bundle bundle = message.getData();
|
||||
Bitmap barcode = bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
|
||||
int duration = message.arg1;
|
||||
mActivity.handleDecode((Result) message.obj, barcode, duration);
|
||||
mActivity.handleDecode((Result) message.obj, barcode);
|
||||
break;
|
||||
case R.id.decode_failed:
|
||||
// We're decoding as fast as possible, so when one decode fails, start another.
|
||||
|
@ -82,6 +82,10 @@ public final class CaptureActivityHandler extends Handler {
|
|||
mActivity.setResult(Activity.RESULT_OK, (Intent) message.obj);
|
||||
mActivity.finish();
|
||||
break;
|
||||
case R.id.launch_product_query:
|
||||
String url = (String) message.obj;
|
||||
mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,13 @@ public final class LocaleManager {
|
|||
GOOGLE_COUNTRY_TLD.put(Locale.UK, "co.uk");
|
||||
}
|
||||
|
||||
// Google Product Search for mobile is available in fewer countries than web search.
|
||||
private static final Map<Locale,String> GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD;
|
||||
static {
|
||||
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD = new HashMap<Locale,String>();
|
||||
GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.UK, "co.uk");
|
||||
}
|
||||
|
||||
private LocaleManager() {}
|
||||
|
||||
/**
|
||||
|
@ -58,4 +65,20 @@ public final class LocaleManager {
|
|||
return tld;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as above, but specifically for Google Product Search.
|
||||
* @return The top-level domain to use.
|
||||
*/
|
||||
public static String getProductSearchCountryTLD() {
|
||||
Locale locale = Locale.getDefault();
|
||||
if (locale == null) {
|
||||
return DEFAULT_TLD;
|
||||
}
|
||||
String tld = GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.get(locale);
|
||||
if (tld == null) {
|
||||
return DEFAULT_TLD;
|
||||
}
|
||||
return tld;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -253,9 +253,10 @@ public abstract class ResultHandler {
|
|||
LocaleManager.getCountryTLD() + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
|
||||
}
|
||||
|
||||
// Uses the mobile-specific version of Product Search, which is formatted for small screens.
|
||||
public final void openProductSearch(String upc) {
|
||||
Uri uri = Uri.parse("http://www.google." + LocaleManager.getCountryTLD() + "/products?q=" +
|
||||
upc);
|
||||
Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD() +
|
||||
"/m/products?q=" + upc + "&source=zxing");
|
||||
launchIntent(new Intent(Intent.ACTION_VIEW, uri));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue