BarcodeProvider.java

  1package eu.siacs.conversations.services;
  2
  3import android.content.ComponentName;
  4import android.content.ContentProvider;
  5import android.content.ContentValues;
  6import android.content.Context;
  7import android.content.Intent;
  8import android.content.ServiceConnection;
  9import android.database.Cursor;
 10import android.graphics.Bitmap;
 11import android.graphics.Color;
 12import android.net.Uri;
 13import android.os.CancellationSignal;
 14import android.os.IBinder;
 15import android.os.ParcelFileDescriptor;
 16import android.support.annotation.Nullable;
 17import android.util.Log;
 18
 19import com.google.zxing.BarcodeFormat;
 20import com.google.zxing.EncodeHintType;
 21import com.google.zxing.aztec.AztecWriter;
 22import com.google.zxing.common.BitMatrix;
 23
 24import java.io.File;
 25import java.io.FileNotFoundException;
 26import java.io.FileOutputStream;
 27import java.io.OutputStream;
 28import java.util.Hashtable;
 29
 30import eu.siacs.conversations.Config;
 31import eu.siacs.conversations.entities.Account;
 32import eu.siacs.conversations.utils.CryptoHelper;
 33import eu.siacs.conversations.xmpp.jid.Jid;
 34
 35public class BarcodeProvider extends ContentProvider implements ServiceConnection {
 36
 37    private static final String AUTHORITY = ".barcodes";
 38
 39    private final Object lock = new Object();
 40
 41    private XmppConnectionService mXmppConnectionService;
 42    private boolean mBindingInProcess = false;
 43
 44    @Override
 45    public boolean onCreate() {
 46        File barcodeDirectory = new File(getContext().getCacheDir().getAbsolutePath() + "/barcodes/");
 47        if (barcodeDirectory.exists() && barcodeDirectory.isDirectory()) {
 48            for (File file : barcodeDirectory.listFiles()) {
 49                if (file.isFile() && !file.isHidden()) {
 50                    Log.d(Config.LOGTAG, "deleting old barcode file " + file.getAbsolutePath());
 51                    file.delete();
 52                }
 53            }
 54        }
 55        return true;
 56    }
 57
 58    @Nullable
 59    @Override
 60    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
 61        return null;
 62    }
 63
 64    @Nullable
 65    @Override
 66    public String getType(Uri uri) {
 67        return "image/png";
 68    }
 69
 70    @Nullable
 71    @Override
 72    public Uri insert(Uri uri, ContentValues values) {
 73        return null;
 74    }
 75
 76    @Override
 77    public int delete(Uri uri, String selection, String[] selectionArgs) {
 78        return 0;
 79    }
 80
 81    @Override
 82    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
 83        return 0;
 84    }
 85
 86    @Override
 87    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
 88        return openFile(uri, mode, null);
 89    }
 90
 91    @Override
 92    public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal) throws FileNotFoundException {
 93        Log.d(Config.LOGTAG, "opening file with uri (normal): " + uri.toString());
 94        String path = uri.getPath();
 95        if (path != null && path.endsWith(".png") && path.length() >= 5) {
 96            String jid = path.substring(1).substring(0, path.length() - 4);
 97            Log.d(Config.LOGTAG, "account:" + jid);
 98            if (connectAndWait()) {
 99                Log.d(Config.LOGTAG, "connected to background service");
100                try {
101                    Account account = mXmppConnectionService.findAccountByJid(Jid.fromString(jid));
102                    if (account != null) {
103                        String shareableUri = account.getShareableUri();
104                        String hash = CryptoHelper.getFingerprint(shareableUri);
105                        File file = new File(getContext().getCacheDir().getAbsolutePath() + "/barcodes/" + hash);
106                        if (!file.exists()) {
107                            file.getParentFile().mkdirs();
108                            file.createNewFile();
109                            Bitmap bitmap = createAztecBitmap(account.getShareableUri(), 1024);
110                            OutputStream outputStream = new FileOutputStream(file);
111                            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
112                            outputStream.close();
113                            outputStream.flush();
114                        }
115                        return ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);
116                    }
117                } catch (Exception e) {
118                    throw new FileNotFoundException();
119                }
120            }
121        }
122        throw new FileNotFoundException();
123    }
124
125    private boolean connectAndWait() {
126        Intent intent = new Intent(getContext(), XmppConnectionService.class);
127        intent.setAction(this.getClass().getSimpleName());
128        Context context = getContext();
129        if (context != null) {
130            synchronized (this) {
131                if (mXmppConnectionService == null && !mBindingInProcess) {
132                    Log.d(Config.LOGTAG,"calling to bind service");
133                    context.startService(intent);
134                    context.bindService(intent, this, Context.BIND_AUTO_CREATE);
135                    this.mBindingInProcess = true;
136                }
137            }
138            try {
139                waitForService();
140                return true;
141            } catch (InterruptedException e) {
142                return false;
143            }
144        } else {
145            Log.d(Config.LOGTAG, "context was null");
146            return false;
147        }
148    }
149
150    @Override
151    public void onServiceConnected(ComponentName name, IBinder service) {
152        synchronized (this) {
153            XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
154            mXmppConnectionService = binder.getService();
155            mBindingInProcess = false;
156            synchronized (this.lock) {
157                lock.notifyAll();
158            }
159        }
160    }
161
162    @Override
163    public void onServiceDisconnected(ComponentName name) {
164        synchronized (this) {
165            mXmppConnectionService = null;
166        }
167    }
168
169    private void waitForService() throws InterruptedException {
170        if (mXmppConnectionService == null) {
171            synchronized (this.lock) {
172                lock.wait();
173            }
174        } else {
175            Log.d(Config.LOGTAG,"not waiting for service because already initialized");
176        }
177    }
178
179    public static Uri getUriForAccount(Context context, Account account) {
180        final String packageId = context.getPackageName();
181        return Uri.parse("content://" + packageId + AUTHORITY + "/" + account.getJid().toBareJid() + ".png");
182    }
183
184    public static Bitmap createAztecBitmap(String input, int size) {
185        try {
186            final AztecWriter AZTEC_WRITER = new AztecWriter();
187            final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
188            hints.put(EncodeHintType.ERROR_CORRECTION, 10);
189            final BitMatrix result = AZTEC_WRITER.encode(input, BarcodeFormat.AZTEC, size, size, hints);
190            final int width = result.getWidth();
191            final int height = result.getHeight();
192            final int[] pixels = new int[width * height];
193            for (int y = 0; y < height; y++) {
194                final int offset = y * width;
195                for (int x = 0; x < width; x++) {
196                    pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
197                }
198            }
199            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
200            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
201            return bitmap;
202        } catch (final Exception e) {
203            return null;
204        }
205    }
206}