mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 19:57:27 -08:00
Back-port BS+ changes to use newer Android 2.x APIs, removing deprecations. Now, we're on to Android 2.1 / Eclair / android-7 as a minimum platform level. I called this "4.0" accordingly.
git-svn-id: https://zxing.googlecode.com/svn/trunk@2031 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
cedfd4c618
commit
898757898c
|
@ -14,18 +14,13 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- versionCode is a monotonically increasing integer. I'm starting it at 6 because v2.3 is the 6th
|
||||
version to be published. The next versionCode will be 7, regardless of whether the user-visible
|
||||
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="3.7"
|
||||
android:versionCode="74"
|
||||
android:versionName="4.0"
|
||||
android:versionCode="75"
|
||||
android:installLocation="auto">
|
||||
<!-- We require Cupcake (Android 1.5) or later, but are really targeting Donut. -->
|
||||
<uses-sdk android:minSdkVersion="3"
|
||||
android:targetSdkVersion="4"/>
|
||||
<uses-sdk android:minSdkVersion="7"
|
||||
android:targetSdkVersion="7"/>
|
||||
<!-- Donut-specific flags which allow us to run on any dpi screens. -->
|
||||
<supports-screens android:xlargeScreens="true"
|
||||
android:largeScreens="true"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.google.zxing.client.android;
|
||||
|
||||
import android.provider.Contacts;
|
||||
import android.provider.ContactsContract;
|
||||
|
||||
/**
|
||||
* The set of constants to use when sending Barcode Scanner an Intent which requests a barcode
|
||||
|
@ -88,27 +88,27 @@ public final class Contents {
|
|||
* phone numbers and addresses.
|
||||
*/
|
||||
public static final String[] PHONE_KEYS = {
|
||||
Contacts.Intents.Insert.PHONE,
|
||||
Contacts.Intents.Insert.SECONDARY_PHONE,
|
||||
Contacts.Intents.Insert.TERTIARY_PHONE
|
||||
ContactsContract.Intents.Insert.PHONE,
|
||||
ContactsContract.Intents.Insert.SECONDARY_PHONE,
|
||||
ContactsContract.Intents.Insert.TERTIARY_PHONE
|
||||
};
|
||||
|
||||
public static final String[] PHONE_TYPE_KEYS = {
|
||||
Contacts.Intents.Insert.PHONE_TYPE,
|
||||
Contacts.Intents.Insert.SECONDARY_PHONE_TYPE,
|
||||
Contacts.Intents.Insert.TERTIARY_PHONE_TYPE
|
||||
ContactsContract.Intents.Insert.PHONE_TYPE,
|
||||
ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE,
|
||||
ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE
|
||||
};
|
||||
|
||||
public static final String[] EMAIL_KEYS = {
|
||||
Contacts.Intents.Insert.EMAIL,
|
||||
Contacts.Intents.Insert.SECONDARY_EMAIL,
|
||||
Contacts.Intents.Insert.TERTIARY_EMAIL
|
||||
ContactsContract.Intents.Insert.EMAIL,
|
||||
ContactsContract.Intents.Insert.SECONDARY_EMAIL,
|
||||
ContactsContract.Intents.Insert.TERTIARY_EMAIL
|
||||
};
|
||||
|
||||
public static final String[] EMAIL_TYPE_KEYS = {
|
||||
Contacts.Intents.Insert.EMAIL_TYPE,
|
||||
Contacts.Intents.Insert.SECONDARY_EMAIL_TYPE,
|
||||
Contacts.Intents.Insert.TERTIARY_EMAIL_TYPE
|
||||
ContactsContract.Intents.Insert.EMAIL_TYPE,
|
||||
ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE,
|
||||
ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.google.zxing.client.android.encode;
|
||||
|
||||
import android.provider.ContactsContract;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
|
@ -34,7 +35,6 @@ import android.content.Intent;
|
|||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Contacts;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -242,13 +242,13 @@ final class QRCodeEncoder {
|
|||
|
||||
newContents.append("MECARD:");
|
||||
|
||||
String name = trim(bundle.getString(Contacts.Intents.Insert.NAME));
|
||||
String name = trim(bundle.getString(ContactsContract.Intents.Insert.NAME));
|
||||
if (name != null) {
|
||||
newContents.append("N:").append(escapeMECARD(name)).append(';');
|
||||
newDisplayContents.append(name);
|
||||
}
|
||||
|
||||
String address = trim(bundle.getString(Contacts.Intents.Insert.POSTAL));
|
||||
String address = trim(bundle.getString(ContactsContract.Intents.Insert.POSTAL));
|
||||
if (address != null) {
|
||||
newContents.append("ADR:").append(escapeMECARD(address)).append(';');
|
||||
newDisplayContents.append('\n').append(address);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package com.google.zxing.client.android.result;
|
||||
|
||||
import android.util.Log;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.client.android.Contents;
|
||||
import com.google.zxing.client.android.Intents;
|
||||
|
@ -37,7 +36,8 @@ import android.content.SharedPreferences;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Contacts;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
@ -80,23 +80,25 @@ public abstract class ResultHandler {
|
|||
private static final String MARKET_REFERRER_SUFFIX =
|
||||
"&referrer=utm_source%3Dbarcodescanner%26utm_medium%3Dapps%26utm_campaign%3Dscan";
|
||||
|
||||
private static final String[] EMAIL_TYPE_STRINGS = {"home", "work"};
|
||||
private static final String[] PHONE_TYPE_STRINGS = {"home", "work", "mobile", "fax", "pager"};
|
||||
private static final String[] EMAIL_TYPE_STRINGS = {"home", "work", "mobile"};
|
||||
private static final String[] PHONE_TYPE_STRINGS = {"home", "work", "mobile", "fax", "pager", "main"};
|
||||
private static final String[] ADDRESS_TYPE_STRINGS = {"home", "work"};
|
||||
private static final int[] EMAIL_TYPE_VALUES = {
|
||||
Contacts.ContactMethodsColumns.TYPE_HOME,
|
||||
Contacts.ContactMethodsColumns.TYPE_WORK,
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_HOME,
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_WORK,
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_MOBILE,
|
||||
};
|
||||
private static final int[] PHONE_TYPE_VALUES = {
|
||||
Contacts.PhonesColumns.TYPE_HOME,
|
||||
Contacts.PhonesColumns.TYPE_WORK,
|
||||
Contacts.PhonesColumns.TYPE_MOBILE,
|
||||
Contacts.PhonesColumns.TYPE_FAX_WORK,
|
||||
Contacts.PhonesColumns.TYPE_PAGER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_HOME,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_WORK,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_PAGER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MAIN,
|
||||
};
|
||||
private static final int[] ADDRESS_TYPE_VALUES = {
|
||||
Contacts.ContactMethodsColumns.TYPE_HOME,
|
||||
Contacts.ContactMethodsColumns.TYPE_WORK,
|
||||
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME,
|
||||
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK,
|
||||
};
|
||||
private static final int NO_TYPE = -1;
|
||||
|
||||
|
@ -297,14 +299,13 @@ public abstract class ResultHandler {
|
|||
String title) {
|
||||
|
||||
// Only use the first name in the array, if present.
|
||||
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, Contacts.CONTENT_URI);
|
||||
intent.setType(Contacts.People.CONTENT_ITEM_TYPE);
|
||||
putExtra(intent, Contacts.Intents.Insert.NAME, names != null ? names[0] : null);
|
||||
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, ContactsContract.Contacts.CONTENT_URI);
|
||||
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.NAME, names != null ? names[0] : null);
|
||||
|
||||
putExtra(intent, Contacts.Intents.Insert.PHONETIC_NAME, pronunciation);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.PHONETIC_NAME, pronunciation);
|
||||
|
||||
int phoneCount = Math.min(phoneNumbers != null ? phoneNumbers.length : 0,
|
||||
Contents.PHONE_KEYS.length);
|
||||
int phoneCount = Math.min(phoneNumbers != null ? phoneNumbers.length : 0, Contents.PHONE_KEYS.length);
|
||||
for (int x = 0; x < phoneCount; x++) {
|
||||
putExtra(intent, Contents.PHONE_KEYS[x], phoneNumbers[x]);
|
||||
if (phoneTypes != null && x < phoneTypes.length) {
|
||||
|
@ -326,17 +327,17 @@ public abstract class ResultHandler {
|
|||
}
|
||||
}
|
||||
|
||||
putExtra(intent, Contacts.Intents.Insert.NOTES, note);
|
||||
putExtra(intent, Contacts.Intents.Insert.IM_HANDLE, instantMessenger);
|
||||
putExtra(intent, Contacts.Intents.Insert.POSTAL, address);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.NOTES, note);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.IM_HANDLE, instantMessenger);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.POSTAL, address);
|
||||
if (addressType != null) {
|
||||
int type = toAddressContractType(addressType);
|
||||
if (type >= 0) {
|
||||
intent.putExtra(Contacts.Intents.Insert.POSTAL_TYPE, type);
|
||||
intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, type);
|
||||
}
|
||||
}
|
||||
putExtra(intent, Contacts.Intents.Insert.COMPANY, org);
|
||||
putExtra(intent, Contacts.Intents.Insert.JOB_TITLE, title);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.COMPANY, org);
|
||||
putExtra(intent, ContactsContract.Intents.Insert.JOB_TITLE, title);
|
||||
launchIntent(intent);
|
||||
}
|
||||
|
||||
|
@ -448,18 +449,18 @@ public abstract class ResultHandler {
|
|||
|
||||
final void getDirections(double latitude, double longitude) {
|
||||
launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google." +
|
||||
LocaleManager.getCountryTLD(getActivity()) + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
|
||||
LocaleManager.getCountryTLD(activity) + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
|
||||
}
|
||||
|
||||
// Uses the mobile-specific version of Product Search, which is formatted for small screens.
|
||||
final void openProductSearch(String upc) {
|
||||
Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD(getActivity()) +
|
||||
Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD(activity) +
|
||||
"/m/products?q=" + upc + "&source=zxing");
|
||||
launchIntent(new Intent(Intent.ACTION_VIEW, uri));
|
||||
}
|
||||
|
||||
final void openBookSearch(String isbn) {
|
||||
Uri uri = Uri.parse("http://books.google." + LocaleManager.getBookSearchCountryTLD(getActivity()) +
|
||||
Uri uri = Uri.parse("http://books.google." + LocaleManager.getBookSearchCountryTLD(activity) +
|
||||
"/books?vid=isbn" + isbn);
|
||||
launchIntent(new Intent(Intent.ACTION_VIEW, uri));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package com.google.zxing.client.android.share;
|
||||
|
||||
import android.provider.ContactsContract;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.client.android.Contents;
|
||||
import com.google.zxing.client.android.Intents;
|
||||
|
@ -29,7 +30,6 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.Browser;
|
||||
import android.provider.Contacts;
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -51,29 +51,12 @@ public final class ShareActivity extends Activity {
|
|||
private static final int PICK_CONTACT = 1;
|
||||
private static final int PICK_APP = 2;
|
||||
|
||||
//private static final int METHODS_ID_COLUMN = 0;
|
||||
private static final int METHODS_KIND_COLUMN = 1;
|
||||
private static final int METHODS_DATA_COLUMN = 2;
|
||||
|
||||
private static final String[] METHODS_PROJECTION = {
|
||||
BaseColumns._ID, // 0
|
||||
Contacts.ContactMethodsColumns.KIND, // 1
|
||||
Contacts.ContactMethodsColumns.DATA, // 2
|
||||
};
|
||||
|
||||
private static final int PHONES_NUMBER_COLUMN = 1;
|
||||
|
||||
private static final String[] PHONES_PROJECTION = {
|
||||
BaseColumns._ID, // 0
|
||||
Contacts.PhonesColumns.NUMBER // 1
|
||||
};
|
||||
|
||||
private Button clipboardButton;
|
||||
|
||||
private final Button.OnClickListener contactListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, Contacts.People.CONTENT_URI);
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
startActivityForResult(intent, PICK_CONTACT);
|
||||
}
|
||||
|
@ -201,78 +184,106 @@ public final class ShareActivity extends Activity {
|
|||
return; // Show error?
|
||||
}
|
||||
ContentResolver resolver = getContentResolver();
|
||||
Cursor contactCursor;
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
Cursor cursor;
|
||||
try {
|
||||
// We're seeing about six reports a week of this exception although I don't understand why.
|
||||
contactCursor = resolver.query(contactUri, null, null, null, null);
|
||||
cursor = resolver.query(contactUri, null, null, null, null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
Bundle bundle = new Bundle();
|
||||
if (contactCursor != null && contactCursor.moveToFirst()) {
|
||||
int nameColumn = contactCursor.getColumnIndex(Contacts.PeopleColumns.NAME);
|
||||
if (nameColumn >= 0) {
|
||||
String name = contactCursor.getString(nameColumn);
|
||||
if (cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't require a name to be present, this contact might be just a phone number.
|
||||
if (name != null && name.length() > 0) {
|
||||
bundle.putString(Contacts.Intents.Insert.NAME, massageContactData(name));
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Unable to find column? " + Contacts.PeopleColumns.NAME);
|
||||
String id;
|
||||
String name;
|
||||
boolean hasPhone;
|
||||
try {
|
||||
if (!cursor.moveToFirst()) {
|
||||
return;
|
||||
}
|
||||
contactCursor.close();
|
||||
|
||||
Uri phonesUri = Uri.withAppendedPath(contactUri, Contacts.People.Phones.CONTENT_DIRECTORY);
|
||||
Cursor phonesCursor = resolver.query(phonesUri, PHONES_PROJECTION, null, null, null);
|
||||
id = cursor.getString(cursor.getColumnIndex(BaseColumns._ID));
|
||||
name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
|
||||
hasPhone = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0;
|
||||
|
||||
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
// Don't require a name to be present, this contact might be just a phone number.
|
||||
if (name != null && name.length() > 0) {
|
||||
bundle.putString(ContactsContract.Intents.Insert.NAME, massageContactData(name));
|
||||
}
|
||||
|
||||
if (hasPhone) {
|
||||
Cursor phonesCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||
null,
|
||||
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + '=' + id,
|
||||
null,
|
||||
null);
|
||||
if (phonesCursor != null) {
|
||||
int foundPhone = 0;
|
||||
while (phonesCursor.moveToNext()) {
|
||||
String number = phonesCursor.getString(PHONES_NUMBER_COLUMN);
|
||||
if (foundPhone < Contents.PHONE_KEYS.length) {
|
||||
try {
|
||||
int foundPhone = 0;
|
||||
int phonesNumberColumn = phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
while (phonesCursor.moveToNext() && foundPhone < Contents.PHONE_KEYS.length) {
|
||||
String number = phonesCursor.getString(phonesNumberColumn);
|
||||
bundle.putString(Contents.PHONE_KEYS[foundPhone], massageContactData(number));
|
||||
foundPhone++;
|
||||
}
|
||||
} finally {
|
||||
phonesCursor.close();
|
||||
}
|
||||
phonesCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
Uri methodsUri = Uri.withAppendedPath(contactUri,
|
||||
Contacts.People.ContactMethods.CONTENT_DIRECTORY);
|
||||
Cursor methodsCursor = resolver.query(methodsUri, METHODS_PROJECTION, null, null, null);
|
||||
if (methodsCursor != null) {
|
||||
int foundEmail = 0;
|
||||
boolean foundPostal = false;
|
||||
while (methodsCursor.moveToNext()) {
|
||||
int kind = methodsCursor.getInt(METHODS_KIND_COLUMN);
|
||||
String data = methodsCursor.getString(METHODS_DATA_COLUMN);
|
||||
switch (kind) {
|
||||
case Contacts.KIND_EMAIL:
|
||||
if (foundEmail < Contents.EMAIL_KEYS.length) {
|
||||
bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(data));
|
||||
foundEmail++;
|
||||
}
|
||||
break;
|
||||
case Contacts.KIND_POSTAL:
|
||||
if (!foundPostal) {
|
||||
bundle.putString(Contacts.Intents.Insert.POSTAL, massageContactData(data));
|
||||
foundPostal = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Cursor methodsCursor = resolver.query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI,
|
||||
null,
|
||||
ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID + '=' + id,
|
||||
null,
|
||||
null);
|
||||
if (methodsCursor != null) {
|
||||
try {
|
||||
if (methodsCursor.moveToNext()) {
|
||||
String data = methodsCursor.getString(
|
||||
methodsCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS));
|
||||
bundle.putString(ContactsContract.Intents.Insert.POSTAL, massageContactData(data));
|
||||
}
|
||||
} finally {
|
||||
methodsCursor.close();
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intents.Encode.ACTION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT);
|
||||
intent.putExtra(Intents.Encode.DATA, bundle);
|
||||
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
|
||||
|
||||
Log.i(TAG, "Sending bundle for encoding: " + bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
Cursor emailCursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
|
||||
null,
|
||||
ContactsContract.CommonDataKinds.Email.CONTACT_ID + '=' + id,
|
||||
null,
|
||||
null);
|
||||
if (emailCursor != null) {
|
||||
try {
|
||||
int foundEmail = 0;
|
||||
int emailColumn = emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
|
||||
while (emailCursor.moveToNext() && foundEmail < Contents.EMAIL_KEYS.length) {
|
||||
String email = emailCursor.getString(emailColumn);
|
||||
bundle.putString(Contents.EMAIL_KEYS[foundEmail], massageContactData(email));
|
||||
foundEmail++;
|
||||
}
|
||||
} finally {
|
||||
emailCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intents.Encode.ACTION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
intent.putExtra(Intents.Encode.TYPE, Contents.Type.CONTACT);
|
||||
intent.putExtra(Intents.Encode.DATA, bundle);
|
||||
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());
|
||||
|
||||
Log.i(TAG, "Sending bundle for encoding: " + bundle);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private static String massageContactData(String data) {
|
||||
|
|
Loading…
Reference in a new issue