QR codes for profiles

This commit is contained in:
Grishka
2024-02-22 21:35:46 +03:00
parent b1e999cc9c
commit 5cf222379a
46 changed files with 2086 additions and 6 deletions

View File

@@ -0,0 +1,47 @@
package org.joinmastodon.android.googleservices;
import android.app.PendingIntent;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
public class ConnectionResult extends AutoSafeParcelable{
public static final int UNKNOWN = -1;
public static final int SUCCESS = 0;
public static final int SERVICE_MISSING = 1;
public static final int SERVICE_VERSION_UPDATE_REQUIRED = 2;
public static final int SERVICE_DISABLED = 3;
public static final int SIGN_IN_REQUIRED = 4;
public static final int INVALID_ACCOUNT = 5;
public static final int RESOLUTION_REQUIRED = 6;
public static final int NETWORK_ERROR = 7;
public static final int INTERNAL_ERROR = 8;
public static final int SERVICE_INVALID = 9;
public static final int DEVELOPER_ERROR = 10;
public static final int LICENSE_CHECK_FAILED = 11;
public static final int CANCELED = 13;
public static final int TIMEOUT = 14;
public static final int INTERRUPTED = 15;
public static final int API_UNAVAILABLE = 16;
public static final int SIGN_IN_FAILED = 17;
public static final int SERVICE_UPDATING = 18;
public static final int SERVICE_MISSING_PERMISSION = 19;
public static final int RESTRICTED_PROFILE = 20;
public static final int RESOLUTION_ACTIVITY_NOT_FOUND = 22;
public static final int API_DISABLED = 23;
public static final int API_DISABLED_FOR_CONNECTION = 24;
@Deprecated
public static final int DRIVE_EXTERNAL_STORAGE_REQUIRED = 1500;
@SafeParceled(1)
public int versionCode;
@SafeParceled(2)
public int errorCode;
@SafeParceled(3)
public PendingIntent resolution;
@SafeParceled(4)
public String errorMessage;
public static final Creator<ConnectionResult> CREATOR=new AutoCreator<>(ConnectionResult.class);
}

View File

@@ -0,0 +1,116 @@
package org.joinmastodon.android.googleservices;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.google.android.gms.common.internal.ConnectionInfo;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import com.google.android.gms.common.internal.IGmsServiceBroker;
import com.google.android.gms.common.moduleinstall.internal.IModuleInstallService;
import java.util.function.Function;
public class GmsClient{
private static final String TAG="GmsClient";
private static final SparseArray<ServiceConnection> currentConnections=new SparseArray<>();
private static int nextConnectionID=0;
public static <I extends IInterface> void connectToService(Context context, String action, int id, boolean useDynamicLookup, ServiceConnectionCallback<I> callback, Function<IBinder, I> asInterface){
Intent intent;
if(useDynamicLookup){
try{
Bundle args=new Bundle();
args.putString("serviceActionBundleKey", action);
Bundle result=context.getContentResolver().call(new Uri.Builder().scheme("content").authority("com.google.android.gms.chimera").build(), "serviceIntentCall", null, args);
if(result==null)
throw new IllegalStateException("Dynamic lookup failed");
intent=result.getParcelable("serviceResponseIntentKey");
if(intent==null)
throw new IllegalStateException("Dynamic lookup returned null");
}catch(Exception x){
callback.onError(x);
return;
}
}else{
intent=new Intent(action);
}
intent.setPackage("com.google.android.gms");
ServiceConnection conn=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service){
IGmsServiceBroker broker=IGmsServiceBroker.Stub.asInterface(service);
GetServiceRequest req=new GetServiceRequest();
req.serviceId=id;
req.packageName=context.getPackageName();
ServiceConnection serviceConnectionThis=this;
try{
broker.getService(new IGmsCallbacks.Stub(){
@Override
public void onPostInitComplete(int statusCode, IBinder binder, Bundle params) throws RemoteException{
int connectionID=nextConnectionID++;
currentConnections.put(connectionID, serviceConnectionThis);
callback.onSuccess(asInterface.apply(binder), connectionID);
}
@Override
public void onAccountValidationComplete(int statusCode, Bundle params) throws RemoteException{}
@Override
public void onPostInitCompleteWithConnectionInfo(int statusCode, IBinder binder, ConnectionInfo info) throws RemoteException{
onPostInitComplete(statusCode, binder, info!=null ? info.params : null);
}
}, req);
}catch(Exception x){
callback.onError(x);
context.unbindService(this);
}
}
@Override
public void onServiceDisconnected(ComponentName name){}
};
boolean res=context.bindService(intent, conn, Context.BIND_AUTO_CREATE | Context.BIND_DEBUG_UNBIND | Context.BIND_ADJUST_WITH_ACTIVITY);
if(!res){
context.unbindService(conn);
callback.onError(new IllegalStateException("Service connection failed"));
}
}
public static void disconnectFromService(Context context, int connectionID){
ServiceConnection conn=currentConnections.get(connectionID);
if(conn!=null){
currentConnections.remove(connectionID);
context.unbindService(conn);
}
}
public static boolean isGooglePlayServicesAvailable(Context context){
PackageManager pm=context.getPackageManager();
try{
pm.getPackageInfo("com.google.android.gms", 0);
return true;
}catch(PackageManager.NameNotFoundException e){
return false;
}
}
public static void getModuleInstallerService(Context context, ServiceConnectionCallback<IModuleInstallService> callback){
connectToService(context, "com.google.android.gms.chimera.container.moduleinstall.ModuleInstallService.START", 308, true, callback, IModuleInstallService.Stub::asInterface);
}
public interface ServiceConnectionCallback<I extends IInterface>{
void onSuccess(I service, int connectionID);
void onError(Exception error);
}
}

View File

@@ -0,0 +1,253 @@
package org.joinmastodon.android.googleservices.barcodescanner;
import android.graphics.Point;
import org.microg.safeparcel.AutoSafeParcelable;
import org.microg.safeparcel.SafeParceled;
public class Barcode extends AutoSafeParcelable{
public static final int FORMAT_UNKNOWN = -1;
public static final int FORMAT_ALL_FORMATS = 0;
public static final int FORMAT_CODE_128 = 1;
public static final int FORMAT_CODE_39 = 2;
public static final int FORMAT_CODE_93 = 4;
public static final int FORMAT_CODABAR = 8;
public static final int FORMAT_DATA_MATRIX = 16;
public static final int FORMAT_EAN_13 = 32;
public static final int FORMAT_EAN_8 = 64;
public static final int FORMAT_ITF = 128;
public static final int FORMAT_QR_CODE = 256;
public static final int FORMAT_UPC_A = 512;
public static final int FORMAT_UPC_E = 1024;
public static final int FORMAT_PDF417 = 2048;
public static final int FORMAT_AZTEC = 4096;
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_CONTACT_INFO = 1;
public static final int TYPE_EMAIL = 2;
public static final int TYPE_ISBN = 3;
public static final int TYPE_PHONE = 4;
public static final int TYPE_PRODUCT = 5;
public static final int TYPE_SMS = 6;
public static final int TYPE_TEXT = 7;
public static final int TYPE_URL = 8;
public static final int TYPE_WIFI = 9;
public static final int TYPE_GEO = 10;
public static final int TYPE_CALENDAR_EVENT = 11;
public static final int TYPE_DRIVER_LICENSE = 12;
@SafeParceled(1)
public int format;
@SafeParceled(2)
public String displayValue;
@SafeParceled(3)
public String rawValue;
@SafeParceled(4)
public byte[] rawBytes;
@SafeParceled(5)
public Point[] cornerPoints;
@SafeParceled(6)
public int valueType;
@SafeParceled(7)
public Email emailValue;
@SafeParceled(8)
public Phone phoneValue;
@SafeParceled(9)
public SMS smsValue;
@SafeParceled(10)
public WiFi wifiValue;
@SafeParceled(11)
public UrlBookmark urlBookmarkValue;
@SafeParceled(12)
public GeoPoint geoPointValue;
@SafeParceled(13)
public CalendarEvent calendarEventValue;
@SafeParceled(14)
public ContactInfo contactInfoValue;
@SafeParceled(15)
public DriverLicense driverLicenseValue;
public static final Creator<Barcode> CREATOR=new AutoCreator<>(Barcode.class);
// None of the following is needed or used in the Mastodon app and its use cases for QR code scanning,
// but I'm putting it out there in case someone else is crazy enough to want to use Google Services without their libraries
public static class Email extends AutoSafeParcelable{
@SafeParceled(1)
public int type;
@SafeParceled(2)
public String address;
@SafeParceled(3)
public String subject;
@SafeParceled(4)
public String body;
public static final Creator<Email> CREATOR=new AutoCreator<>(Email.class);
}
public static class Phone extends AutoSafeParcelable{
@SafeParceled(1)
public int type;
@SafeParceled(2)
public String number;
public static final Creator<Phone> CREATOR=new AutoCreator<>(Phone.class);
}
public static class SMS extends AutoSafeParcelable{
@SafeParceled(1)
public String message;
@SafeParceled(2)
public String phoneNumber;
public static final Creator<SMS> CREATOR=new AutoCreator<>(SMS.class);
}
public static class WiFi extends AutoSafeParcelable{
@SafeParceled(1)
public String ssid;
@SafeParceled(2)
public String password;
@SafeParceled(3)
public int encryptionType;
public static final Creator<WiFi> CREATOR=new AutoCreator<>(WiFi.class);
}
public static class UrlBookmark extends AutoSafeParcelable{
@SafeParceled(1)
public String title;
@SafeParceled(2)
public String url;
public static final Creator<UrlBookmark> CREATOR=new AutoCreator<>(UrlBookmark.class);
}
public static class GeoPoint extends AutoSafeParcelable{
@SafeParceled(1)
public double lat;
@SafeParceled(2)
public double lng;
public static final Creator<GeoPoint> CREATOR=new AutoCreator<>(GeoPoint.class);
}
public static class EventDateTime extends AutoSafeParcelable{
@SafeParceled(1)
public int year;
@SafeParceled(2)
public int month;
@SafeParceled(3)
public int day;
@SafeParceled(4)
public int hours;
@SafeParceled(5)
public int minutes;
@SafeParceled(6)
public int seconds;
@SafeParceled(7)
public boolean isUtc;
@SafeParceled(8)
public String rawValue;
public static final Creator<EventDateTime> CREATOR=new AutoCreator<>(EventDateTime.class);
}
public static class CalendarEvent extends AutoSafeParcelable{
@SafeParceled(1)
public String summary;
@SafeParceled(2)
public String description;
@SafeParceled(3)
public String location;
@SafeParceled(4)
public String organizer;
@SafeParceled(5)
public String status;
@SafeParceled(6)
public EventDateTime start;
@SafeParceled(7)
public EventDateTime end;
public static final Creator<CalendarEvent> CREATOR=new AutoCreator<>(CalendarEvent.class);
}
public static class Address extends AutoSafeParcelable{
@SafeParceled(1)
public int type;
@SafeParceled(2)
public String[] addressLines;
public static final Creator<Address> CREATOR=new AutoCreator<>(Address.class);
}
public static class PersonName extends AutoSafeParcelable{
@SafeParceled(1)
public String formattedName;
@SafeParceled(2)
public String pronunciation;
@SafeParceled(3)
public String prefix;
@SafeParceled(4)
public String first;
@SafeParceled(5)
public String middle;
@SafeParceled(6)
public String last;
@SafeParceled(7)
public String suffix;
public static final Creator<PersonName> CREATOR=new AutoCreator<>(PersonName.class);
}
public static class ContactInfo extends AutoSafeParcelable{
@SafeParceled(1)
public PersonName name;
@SafeParceled(2)
public String organization;
@SafeParceled(3)
public String title;
@SafeParceled(4)
public Phone[] phones;
@SafeParceled(5)
public Email[] emails;
@SafeParceled(6)
public String[] urls;
@SafeParceled(7)
public Address[] addresses;
public static final Creator<ContactInfo> CREATOR=new AutoCreator<>(ContactInfo.class);
}
public static class DriverLicense extends AutoSafeParcelable{
@SafeParceled(1)
public String documentType;
@SafeParceled(2)
public String firstName;
@SafeParceled(3)
public String middleName;
@SafeParceled(4)
public String lastName;
@SafeParceled(5)
public String gender;
@SafeParceled(6)
public String addressStreet;
@SafeParceled(7)
public String addressCity;
@SafeParceled(8)
public String addressState;
@SafeParceled(9)
public String addressZip;
@SafeParceled(10)
public String licenseNumber;
@SafeParceled(11)
public String issueDate;
@SafeParceled(12)
public String expiryDate;
@SafeParceled(13)
public String birthDate;
@SafeParceled(14)
public String issuingCountry;
public static final Creator<DriverLicense> CREATOR=new AutoCreator<>(DriverLicense.class);
}
}

View File

@@ -0,0 +1,38 @@
package org.joinmastodon.android.googleservices.barcodescanner;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import org.joinmastodon.android.MastodonApp;
public class BarcodeScanner{
public static Intent createIntent(int formats, boolean allowManualInout, boolean enableAutoZoom){
Intent intent=new Intent().setPackage("com.google.android.gms").setAction("com.google.android.gms.mlkit.ACTION_SCAN_BARCODE");
String appName;
ApplicationInfo appInfo=MastodonApp.context.getApplicationInfo();
if(appInfo.labelRes!=0)
appName=MastodonApp.context.getString(appInfo.labelRes);
else
appName=MastodonApp.context.getPackageManager().getApplicationLabel(appInfo).toString();
intent.putExtra("extra_calling_app_name", appName);
intent.putExtra("extra_supported_formats", formats);
intent.putExtra("extra_allow_manual_input", allowManualInout);
intent.putExtra("extra_enable_auto_zoom", enableAutoZoom);
return intent;
}
public static boolean isValidResult(Intent intent){
return intent!=null && intent.hasExtra("extra_barcode_result");
}
public static Barcode getResult(Intent intent){
byte[] serialized=intent.getByteArrayExtra("extra_barcode_result");
Parcel parcel=Parcel.obtain();
parcel.unmarshall(serialized, 0, serialized.length);
parcel.setDataPosition(0);
Barcode barcode=Barcode.CREATOR.createFromParcel(parcel);
parcel.recycle();
return barcode;
}
}