List of usage examples for android.text TextUtils join
public static String join(@NonNull CharSequence delimiter, @NonNull Iterable tokens)
From source file:com.google.android.car.kitchensink.sensor.SensorsTestFragment.java
private void refreshUi() { String summaryString;/*from w w w . j a va 2 s .c o m*/ synchronized (this) { List<String> summary = new ArrayList<>(); for (Integer i : supportedSensors) { CarSensorEvent event = mEventMap.get(i); switch (i) { case CarSensorManager.SENSOR_TYPE_COMPASS: summary.add(getCompassString(event)); break; case CarSensorManager.SENSOR_TYPE_CAR_SPEED: summary.add(getContext().getString(R.string.sensor_speed, getTimestamp(event), event == null ? mNaString : event.getCarSpeedData().carSpeed)); break; case CarSensorManager.SENSOR_TYPE_RPM: summary.add(getContext().getString(R.string.sensor_rpm, getTimestamp(event), event == null ? mNaString : event.getRpmData().rpm)); break; case CarSensorManager.SENSOR_TYPE_ODOMETER: summary.add(getContext().getString(R.string.sensor_odometer, getTimestamp(event), event == null ? mNaString : event.getOdometerData().kms)); break; case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL: String level = mNaString; String range = mNaString; String lowFuelWarning = mNaString; if (event != null) { CarSensorEvent.FuelLevelData fuelData = event.getFuelLevelData(); level = fuelData.level == -1 ? level : String.valueOf(fuelData.level); range = fuelData.range == -1 ? range : String.valueOf(fuelData.range); lowFuelWarning = String.valueOf(fuelData.lowFuelWarning); } summary.add(getContext().getString(R.string.sensor_fuel_level, getTimestamp(event), level, range, lowFuelWarning)); break; case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE: summary.add(getContext().getString(R.string.sensor_parking_brake, getTimestamp(event), event == null ? mNaString : event.getParkingBrakeData().isEngaged)); break; case CarSensorManager.SENSOR_TYPE_GEAR: summary.add(getContext().getString(R.string.sensor_gear, getTimestamp(event), event == null ? mNaString : event.getGearData().gear)); break; case CarSensorManager.SENSOR_TYPE_NIGHT: summary.add(getContext().getString(R.string.sensor_night, getTimestamp(event), event == null ? mNaString : event.getNightData().isNightMode)); break; case CarSensorManager.SENSOR_TYPE_LOCATION: summary.add(getLocationString(event)); break; case CarSensorManager.SENSOR_TYPE_DRIVING_STATUS: String drivingStatus = mNaString; String binDrivingStatus = mNaString; if (event != null) { CarSensorEvent.DrivingStatusData drivingStatusData = event.getDrivingStatusData(); drivingStatus = String.valueOf(drivingStatusData.status); binDrivingStatus = Integer.toBinaryString(drivingStatusData.status); } summary.add(getContext().getString(R.string.sensor_driving_status, getTimestamp(event), drivingStatus, binDrivingStatus)); break; case CarSensorManager.SENSOR_TYPE_ENVIRONMENT: String temperature = mNaString; String pressure = mNaString; if (event != null) { CarSensorEvent.EnvironmentData env = event.getEnvironmentData(); temperature = Float.isNaN(env.temperature) ? temperature : String.valueOf(env.temperature); pressure = Float.isNaN(env.pressure) ? pressure : String.valueOf(env.pressure); } summary.add(getContext().getString(R.string.sensor_environment, getTimestamp(event), temperature, pressure)); break; case CarSensorManager.SENSOR_TYPE_ACCELEROMETER: summary.add(getAccelerometerString(event)); break; case CarSensorManager.SENSOR_TYPE_GPS_SATELLITE: summary.add(getGpsSatelliteString(event)); break; case CarSensorManager.SENSOR_TYPE_GYROSCOPE: summary.add(getGyroscopeString(event)); break; case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE: if (event != null) { CarSensorEvent.CarWheelTickDistanceData d = event.getCarWheelTickDistanceData(); summary.add(getContext().getString(R.string.sensor_wheel_ticks, getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm, d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm, d.rearRightWheelDistanceMm)); } else { summary.add(getContext().getString(R.string.sensor_wheel_ticks, getTimestamp(event), mNaString, mNaString, mNaString, mNaString, mNaString)); } // Get the config data try { CarSensorConfig c = mSensorManager .getSensorConfig(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE); summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg, c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS), c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK), c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK), c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK), c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK))); } catch (CarNotConnectedException e) { Log.e(TAG, "Car not connected or not supported", e); } break; case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE: summary.add(getContext().getString(R.string.sensor_abs_is_active, getTimestamp(event), event == null ? mNaString : event.getCarAbsActiveData().absIsActive)); break; case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE: summary.add(getContext().getString(R.string.sensor_traction_control_is_active, getTimestamp(event), event == null ? mNaString : event.getCarTractionControlActiveData().tractionControlIsActive)); break; default: // Should never happen. Log.w(TAG, "Unrecognized event type: " + i); } } summaryString = TextUtils.join("\n", summary); } mHandler.post(new Runnable() { @Override public void run() { mSensorInfo.setText(summaryString); } }); }
From source file:com.openerp.addons.messages.MessageDetail.java
private boolean setupMessageDetail(int message_id) { messages_sorted = new ArrayList<OEListViewRows>(); String query = "select t1.id as message_id , t1.*, t2.id as partner_id, t2.name, t2.image_small as image, t2.email from mail_message t1, res_partner t2 where (t1.id = ? or t1.parent_id = ?) and (t2.id = t1.author_id or t1.author_id = 'false') group by t1.id order by t1.date desc"; List<HashMap<String, Object>> records = db.executeSQL(query, new String[] { String.valueOf(message_id), String.valueOf(message_id) }); HashMap<String, Object> row = new HashMap<String, Object>(); row.put("total", records.size()); row.put("records", records); if ((Integer) row.get("total") > 0) { List<HashMap<String, Object>> rows_detail = (List<HashMap<String, Object>>) row.get("records"); for (HashMap<String, Object> row_detail : rows_detail) { int msg_id = Integer.parseInt(row_detail.get("message_id").toString()); String key = row_detail.get("parent_id").toString(); OEListViewRows rowObj = null; String[] ids = getPartnersOfMessage(row_detail.get("message_id").toString()); String partners = "nobody"; if (ids != null) { partners = TextUtils.join(", ", ids); }/*from w w w.j a v a 2 s .c o m*/ row_detail.put("partners", partners); if (key.equals("false")) { // Parent Message if (row_detail.get("author_id").toString().equals("false")) { row_detail.put("image", "false"); } rowObj = new OEListViewRows(msg_id, row_detail); parent_row = row_detail; if (!row_detail.get("model").toString().equals("false")) { messages_sorted.add(rowObj); } else { messages_sorted.add(0, rowObj); } String sub = rowObj.getRow_data().get("subject").toString(); if (sub.equals("false")) { sub = rowObj.getRow_data().get("type").toString(); } TextView txvTitle = (TextView) rootView.findViewById(R.id.txvMessageTitle); txvTitle.setText(sub); if (row_detail.get("model").toString().equals("mail.group")) { if (UserGroups.menu_color.containsKey("group_" + row_detail.get("res_id").toString())) { View tagColor = rootView.findViewById(R.id.groupColorLine); tagColor.setBackgroundColor( UserGroups.menu_color.get("group_" + row_detail.get("res_id").toString())); } } } else { rowObj = new OEListViewRows(msg_id, row_detail); messages_sorted.add(rowObj); } } } return setupListView(messages_sorted); }
From source file:com.github.chenxiaolong.dualbootpatcher.switcher.service.MbtoolTask.java
private boolean performBackupRestore(BackupRestoreParams params, MbtoolInterface iface) throws IOException, MbtoolCommandException, MbtoolException { printSeparator();//from ww w .j a va 2s. c o m switch (params.getAction()) { case BACKUP: printBoldText(Color.MAGENTA, "Backup:\n"); break; case RESTORE: printBoldText(Color.MAGENTA, "Restore:\n"); break; } printBoldText(Color.MAGENTA, "- ROM ID: " + params.getRomId() + "\n"); printBoldText(Color.MAGENTA, "- Targets: " + Arrays.toString(params.getTargets()) + "\n"); printBoldText(Color.MAGENTA, "- Backup name: " + params.getBackupName() + "\n"); printBoldText(Color.MAGENTA, "- Backup dir URI: " + params.getBackupDirUri() + "\n"); printBoldText(Color.MAGENTA, "- Force/overwrite: " + params.getForce() + "\n"); printSeparator(); printBoldText(Color.YELLOW, "Extracting patcher data archive\n"); PatcherUtils.extractPatcher(getContext()); printSeparator(); String backupDir = null; if (params.getBackupDirUri() != null) { backupDir = getPathFromUri(params.getBackupDirUri(), iface); if (backupDir == null) { return false; } } String abi; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { abi = Build.SUPPORTED_ABIS[0]; } else { abi = Build.CPU_ABI; } File mbtoolRecovery = new File(PatcherUtils.getTargetDirectory(getContext()) + File.separator + "binaries" + File.separator + "android" + File.separator + abi + File.separator + "mbtool_recovery"); File mbtoolRecoverySig = new File( PatcherUtils.getTargetDirectory(getContext()) + File.separator + "binaries" + File.separator + "android" + File.separator + abi + File.separator + "mbtool_recovery.sig"); String argv0; switch (params.getAction()) { case BACKUP: argv0 = "backup"; break; case RESTORE: argv0 = "restore"; break; default: throw new IllegalStateException("Invalid action: " + params.getAction()); } ArrayList<String> args = new ArrayList<>(); args.add("-r"); args.add(params.getRomId()); args.add("-t"); args.add(StringUtils.join(params.getTargets(), ',')); if (params.getBackupName() != null) { args.add("-n"); args.add(params.getBackupName()); } if (backupDir != null) { args.add("-d"); args.add(translateEmulatedPath(backupDir)); } if (params.getForce()) { args.add("-f"); } String[] argsArray = args.toArray(new String[args.size()]); printBoldText(Color.YELLOW, "Running " + argv0 + " with arguments: [" + TextUtils.join(", ", args) + "]\n"); SignedExecCompletion completion = iface.signedExec(mbtoolRecovery.getAbsolutePath(), mbtoolRecoverySig.getAbsolutePath(), argv0, argsArray, this); switch (completion.result) { case SignedExecResult.PROCESS_EXITED: printBoldText(completion.exitStatus == 0 ? Color.GREEN : Color.RED, "\nCommand returned: " + completion.exitStatus + "\n"); return completion.exitStatus == 0; case SignedExecResult.PROCESS_KILLED_BY_SIGNAL: printBoldText(Color.RED, "\nProcess killed by signal: " + completion.termSig + "\n"); return false; case SignedExecResult.INVALID_SIGNATURE: printBoldText(Color.RED, "\nThe mbtool binary has an invalid signature. This" + " means DualBootPatcher's data files have been maliciously modified" + " by somebody or another app. The backup/restore process has been" + " cancelled.\n"); return false; case SignedExecResult.OTHER_ERROR: default: printBoldText(Color.RED, "\nError: " + completion.errorMsg + "\n"); return false; } }
From source file:cm.aptoide.com.facebook.android.Facebook.java
/** * Internal method to handle dialog-based authentication backend for * authorize().//from w w w . jav a 2 s . c om * * @param activity * The Android Activity that will parent the auth dialog. * @param applicationId * The Facebook application identifier. * @param permissions * A list of permissions required for this application. If you do * not require any permissions, pass an empty String array. */ private void startDialogAuth(Activity activity, String[] permissions) { Bundle params = new Bundle(); if (permissions.length > 0) { params.putString("scope", TextUtils.join(",", permissions)); } CookieSyncManager.createInstance(activity); dialog(activity, LOGIN, params, new DialogListener() { public void onComplete(Bundle values) { // ensure any cookies set by the dialog are saved CookieSyncManager.getInstance().sync(); setAccessToken(values.getString(TOKEN)); setAccessExpiresIn(values.getString(EXPIRES)); if (isSessionValid()) { Util.logd("Facebook-authorize", "Login Success! access_token=" + getAccessToken() + " expires=" + getAccessExpires()); mAuthDialogListener.onComplete(values); } else { mAuthDialogListener.onFacebookError(new FacebookError("Failed to receive access token.")); } } public void onError(DialogError error) { Util.logd("Facebook-authorize", "Login failed: " + error); mAuthDialogListener.onError(error); } public void onFacebookError(FacebookError error) { Util.logd("Facebook-authorize", "Login failed: " + error); mAuthDialogListener.onFacebookError(error); } public void onCancel() { Util.logd("Facebook-authorize", "Login canceled"); mAuthDialogListener.onCancel(); } }); }
From source file:org.ale.scanner.zotero.MainActivity.java
public void loadGroups() { if (mAccountAccess.getGroupCount() == 0 && mAccountAccess.canWriteLibrary()) { mGroups = new SparseArray<PString>(); mGroups.put(Group.GROUP_LIBRARY, new PString(getString(R.string.my_library))); return;/* w ww . j a va2 s . c o m*/ } // Check that we have all the group titles new Thread(new Runnable() { public void run() { final SparseArray<PString> newGroupList = new SparseArray<PString>(); Set<Integer> groups = mAccountAccess.getGroupIds(); if (mAccountAccess.canWriteLibrary()) { newGroupList.put(Group.GROUP_LIBRARY, new PString(getString(R.string.my_library))); } String[] selection = new String[] { TextUtils.join(",", groups) }; Cursor c = getContentResolver().query(Database.GROUP_URI, new String[] { Group._ID, Group.COL_TITLE }, Group._ID + " IN (?)", selection, null); // Figure out which groups we don't have c.moveToFirst(); while (!c.isAfterLast()) { int haveGroupId = c.getInt(0); groups.remove(haveGroupId); newGroupList.put(haveGroupId, new PString(c.getString(1))); c.moveToNext(); } c.close(); // Update the group list mUIThreadHandler.post(new Runnable() { public void run() { mGroups = newGroupList; } }); // If we have any unknown groups, do a group lookup. if (groups.size() > 0) { // Make new database entries for new groups. Mapping each // id to "<Group ID>" temporarily. ContentValues[] values = new ContentValues[groups.size()]; int i = 0; for (Integer gid : groups) { values[i] = new ContentValues(); values[i].put(Group._ID, gid); values[i].put(Group.COL_TITLE, "<" + gid + ">"); i++; } getContentResolver().bulkInsert(Database.GROUP_URI, values); mZAPI.getGroups(); } } }).start(); }
From source file:com.gh4a.activities.IssueListActivity.java
private Fragment makeListFragment(int position) { Map<String, String> filterData = new HashMap<>(); filterData.put("sort", mSortHelper.getSortMode()); filterData.put("direction", mSortHelper.getSortDirection()); filterData.put("q", String.format(Locale.US, LIST_QUERY, position == 1 ? Constants.Issue.STATE_CLOSED : Constants.Issue.STATE_OPEN, mRepoOwner, mRepoName, mSelectedLabels != null ? "labels:" + TextUtils.join(",", mSelectedLabels) : "", mSelectedMilestone > 0 ? "milestone:" + mSelectedMilestone : "", // XXX: milestone name? mSelectedAssignee != null ? "assignee:" + mSelectedAssignee : "")); final IssueListFragment f = IssueListFragment.newInstance(filterData, position == 1, R.string.no_issues_found, false); if (position == 1) { mClosedFragment = f;//from w w w. j a va 2 s . c o m } else { mOpenFragment = f; } return f; }
From source file:com.microsoft.services.msa.LiveAuthClient.java
/** * Logs in an user with the given scopes and additional saved state. * * login displays a {@link Dialog} that will prompt the * user for a username and password, and ask for consent to use the given scopes. * A {@link LiveConnectSession} will be returned by calling * {@link LiveAuthListener#onAuthComplete(LiveStatus, LiveConnectSession, Object)}. * Otherwise, the {@link LiveAuthListener#onAuthError(LiveAuthException, Object)} will be * called. These methods will be called on the main/UI thread. * * @param activity {@link Activity} instance to display the Login dialog on * @param scopes to initialize the {@link LiveConnectSession} with. * See <a href="http://msdn.microsoft.com/en-us/library/hh243646.aspx">MSDN Live Connect * Reference's Scopes and permissions</a> for a list of scopes and explanations. * Scopes specified here override scopes specified in the constructor. * @param userState arbitrary object that is used to determine the caller of the method. * @param loginHint the hint for the sign in experience to show the username pre-filled out * @param listener called on either completion or error during the login process. * @throws IllegalStateException if there is a pending login request. */// w ww. j av a 2 s .c om public void login(Activity activity, Iterable<String> scopes, Object userState, String loginHint, LiveAuthListener listener) { LiveConnectUtils.assertNotNull(activity, "activity"); if (listener == null) { listener = NULL_LISTENER; } if (this.hasPendingLoginRequest) { throw new IllegalStateException(ErrorMessages.LOGIN_IN_PROGRESS); } // if no scopes were passed in, use the scopes from initialize or if those are empty, // create an empty list if (scopes == null) { if (this.baseScopes == null) { scopes = Arrays.asList(new String[0]); } else { scopes = this.baseScopes; } } // if the session is valid and contains all the scopes, do not display the login ui. if (loginSilent(scopes, userState, listener)) { Log.i(TAG, "Interactive login not required."); return; } // silent login failed, initiating interactive login String scope = TextUtils.join(OAuth.SCOPE_DELIMITER, scopes); AuthorizationRequest request = new AuthorizationRequest(activity, this.httpClient, this.clientId, scope, loginHint, mOAuthConfig); request.addObserver(new ListenerCallerObserver(listener, userState)); request.addObserver(new RefreshTokenWriter()); request.addObserver(new OAuthRequestObserver() { @Override public void onException(LiveAuthException exception) { LiveAuthClient.this.hasPendingLoginRequest = false; } @Override public void onResponse(OAuthResponse response) { LiveAuthClient.this.hasPendingLoginRequest = false; } }); this.hasPendingLoginRequest = true; request.execute(); }
From source file:com.ichi2.libanki.Sched.java
/** * Returns [deckname, did, rev, lrn, new] *//*from w w w .j ava2 s. c o m*/ public List<DeckDueTreeNode> deckDueList() { _checkDay(); mCol.getDecks().recoverOrphans(); ArrayList<JSONObject> decks = mCol.getDecks().allSorted(); HashMap<String, Integer[]> lims = new HashMap<String, Integer[]>(); ArrayList<DeckDueTreeNode> data = new ArrayList<DeckDueTreeNode>(); try { for (JSONObject deck : decks) { // if we've already seen the exact same deck name, remove the // invalid duplicate and reload if (lims.containsKey(deck.getString("name"))) { mCol.getDecks().rem(deck.getLong("id"), false, true); return deckDueList(); } String p; List<String> parts = Arrays.asList(deck.getString("name").split("::", -1)); if (parts.size() < 2) { p = null; } else { parts = parts.subList(0, parts.size() - 1); p = TextUtils.join("::", parts); } // new int nlim = _deckNewLimitSingle(deck); if (!TextUtils.isEmpty(p)) { if (!lims.containsKey(p)) { // if parent was missing, this deck is invalid, and we need to reload the deck list mCol.getDecks().rem(deck.getLong("id"), false, true); return deckDueList(); } nlim = Math.min(nlim, lims.get(p)[0]); } int _new = _newForDeck(deck.getLong("id"), nlim); // learning int lrn = _lrnForDeck(deck.getLong("id")); // reviews int rlim = _deckRevLimitSingle(deck); if (!TextUtils.isEmpty(p)) { rlim = Math.min(rlim, lims.get(p)[1]); } int rev = _revForDeck(deck.getLong("id"), rlim); // save to list data.add(new DeckDueTreeNode(deck.getString("name"), deck.getLong("id"), rev, lrn, _new)); // add deck as a parent lims.put(deck.getString("name"), new Integer[] { nlim, rlim }); } } catch (JSONException e) { throw new RuntimeException(e); } return data; }
From source file:info.wncwaterfalls.app.ResultsActivity.java
@Override public Bundle onWaterfallQuery() { // Set up our query ArrayList<String> whereList = new ArrayList<String>(); // To hold chunks of the WHERE clause ArrayList<String> argList = new ArrayList<String>(); // To hold our args switch (mSearchMode) { // See which terms we're going to need to query case SearchActivity.SEARCH_MODE_WATERFALL: whereList.add("name like ?"); argList.add('%' + searchTerm.trim() + '%'); break;// w w w . j a v a 2s . co m case SearchActivity.SEARCH_MODE_HIKE: whereList.add("trail_length <= ?"); argList.add(String.valueOf(searchTrailLength)); whereList.add("trail_difficulty_num <= ?"); argList.add(String.valueOf(searchTrailDifficulty)); whereList.add("trail_climb_num <= ?"); argList.add(String.valueOf(searchTrailClimb)); break; case SearchActivity.SEARCH_MODE_LOCATION: if (mFoundOrigin) { // Mi -> M double rangeMeters = searchLocationDistance * 1609.34; // Calculate our bounding box Location pn = calculatePositionAtRange(mOriginLocation, rangeMeters, 0); Location pe = calculatePositionAtRange(mOriginLocation, rangeMeters, 90); Location ps = calculatePositionAtRange(mOriginLocation, rangeMeters, 180); Location pw = calculatePositionAtRange(mOriginLocation, rangeMeters, 270); // Greater than S latitude whereList.add("geo_lat > ?"); argList.add(String.valueOf(ps.getLatitude())); // Less than N latitude whereList.add("geo_lat < ?"); argList.add(String.valueOf(pn.getLatitude())); // Less than E longitude whereList.add("geo_lon < ?"); argList.add(String.valueOf(pe.getLongitude())); // Greater than W longitude whereList.add("geo_lon > ?"); argList.add(String.valueOf(pw.getLongitude())); } else { // Make sure no results are returned, and a toast should happen // on the map side notifying the user as such. whereList.add("_id = ?"); argList.add(""); } // Requester can filter results to actual radius using // android.location.Location.distanceTo() break; } // Restrict to only shared falls if box checked if (searchOnlyShared != null && searchOnlyShared) { whereList.add("shared=1"); } String tables = "waterfalls"; // Select all String[] columns = AttrDatabase.COLUMNS.toArray(new String[AttrDatabase.COLUMNS.size()]); String and = " AND "; // To join our where clause String whereClause = TextUtils.join(and, whereList); String query = SQLiteQueryBuilder.buildQueryString(false, tables, columns, whereClause, null, null, "name ASC", null); Bundle qBundle = new Bundle(); qBundle.putString("query", query); String[] args = argList.toArray(new String[argList.size()]); qBundle.putStringArray("args", args); return qBundle; }
From source file:com.google.android.apps.muzei.provider.MuzeiProvider.java
private int deleteArtwork(@NonNull final Uri uri, final String selection, final String[] selectionArgs) { // Opens the database object in "write" mode. final SQLiteDatabase db = databaseHelper.getWritableDatabase(); String finalWhere = selection; if (MuzeiProvider.uriMatcher.match(uri) == ARTWORK_ID) { finalWhere = MuzeiContract.Artwork.TABLE_NAME + "." + BaseColumns._ID + " = " + uri.getLastPathSegment(); // If there were additional selection criteria, append them to the final WHERE clause if (selection != null) finalWhere = finalWhere + " AND " + selection; }//from ww w . j av a 2s . c o m // We can't just simply delete the rows as that won't free up the space occupied by the // artwork image files associated with each row being deleted. Instead we have to query // and manually delete each artwork file String[] projection = new String[] { MuzeiContract.Artwork.TABLE_NAME + "." + BaseColumns._ID, MuzeiContract.Artwork.COLUMN_NAME_IMAGE_URI, MuzeiContract.Artwork.COLUMN_NAME_TOKEN }; Cursor rowsToDelete = queryArtwork(uri, projection, finalWhere, selectionArgs, MuzeiContract.Artwork.COLUMN_NAME_IMAGE_URI); if (rowsToDelete == null) { return 0; } // First we build a list of IDs to be deleted. This will be used if we need to determine // if a given image URI needs to be deleted List<String> idsToDelete = new ArrayList<>(); rowsToDelete.moveToFirst(); while (!rowsToDelete.isAfterLast()) { idsToDelete.add(Long.toString(rowsToDelete.getLong(0))); rowsToDelete.moveToNext(); } String notInDeleteIds = MuzeiContract.Artwork.TABLE_NAME + "." + BaseColumns._ID + " NOT IN (" + TextUtils.join(",", idsToDelete) + ")"; // Now we actually go through the list of rows to be deleted // and check if we can delete the artwork image file associated with each one rowsToDelete.moveToFirst(); while (!rowsToDelete.isAfterLast()) { Uri artworkUri = ContentUris.withAppendedId(MuzeiContract.Artwork.CONTENT_URI, rowsToDelete.getLong(0)); String imageUri = rowsToDelete.getString(1); String token = rowsToDelete.getString(2); if (TextUtils.isEmpty(imageUri) && TextUtils.isEmpty(token)) { // An empty image URI and token means the artwork is unique to this specific row // so we can always delete it when the associated row is deleted File artwork = getCacheFileForArtworkUri(artworkUri); if (artwork != null && artwork.exists()) { artwork.delete(); } } else if (TextUtils.isEmpty(imageUri)) { // Check if there are other rows using this same token that aren't // in the list of ids to delete Cursor otherArtwork = queryArtwork(MuzeiContract.Artwork.CONTENT_URI, new String[] { MuzeiContract.Artwork.TABLE_NAME + "." + BaseColumns._ID }, MuzeiContract.Artwork.COLUMN_NAME_TOKEN + "=? AND " + notInDeleteIds, new String[] { token }, null); if (otherArtwork == null) { continue; } if (otherArtwork.getCount() == 0) { // There's no non-deleted rows that reference this same artwork URI // so we can delete the artwork File artwork = getCacheFileForArtworkUri(artworkUri); if (artwork != null && artwork.exists()) { artwork.delete(); } } otherArtwork.close(); } else { // Check if there are other rows using this same image URI that aren't // in the list of ids to delete Cursor otherArtwork = queryArtwork(MuzeiContract.Artwork.CONTENT_URI, new String[] { MuzeiContract.Artwork.TABLE_NAME + "." + BaseColumns._ID }, MuzeiContract.Artwork.COLUMN_NAME_IMAGE_URI + "=? AND " + notInDeleteIds, new String[] { imageUri }, null); if (otherArtwork == null) { continue; } if (otherArtwork.getCount() == 0) { // There's no non-deleted rows that reference this same artwork URI // so we can delete the artwork File artwork = getCacheFileForArtworkUri(artworkUri); if (artwork != null && artwork.exists()) { artwork.delete(); } } otherArtwork.close(); } rowsToDelete.moveToNext(); } rowsToDelete.close(); int count = db.delete(MuzeiContract.Artwork.TABLE_NAME, finalWhere, selectionArgs); if (count > 0) { notifyChange(uri); } return count; }