So far I've been making applications with set as Ringtone feature by creating 1 activity for 1 file. It was bad because with apps with more than 20 ringtones I would've needed 20 activities which would affect app size and performance. Then I found that there is a way to do that with only 1 activity and layout, passing data with Intents. Now I have pretty good idea how that works except one thing that bothers me. That is how do I define strings.
I need 1 string for name and 1 for file path
My code:
Boolean success = false;
rsound = new File(rpath, "Slow tone.mp3");rpath.mkdirs(); //Copied file name
if (!rsound.exists()) {
try {
InputStream in = getResources().openRawResource(R.raw.s8slowtone); //path for file
FileOutputStream out = new FileOutputStream(rsound.getPath());
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
}
} catch (Exception e) {
success = false;
}
} else {
success = true;
setRingtone();
}
if (!success) {
setRingtone();
}
}
private void setRingtone() {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, rsound.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, "Slow tone"); //Ringtone name
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/*");
values.put(MediaStore.Audio.Media.ARTIST, " ");
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false);
values.put(MediaStore.Audio.Media.IS_ALARM, false);
values.put(MediaStore.Audio.Media.IS_MUSIC, true);
Uri uri = MediaStore.Audio.Media.getContentUriForPath(rsound.getAbsolutePath());
getContentResolver().delete(uri, MediaStore.MediaColumns.DATA + "=\"" + rsound.getAbsolutePath() + "\"",
null);
Uri newUri = getContentResolver().insert(uri, values);
RingtoneManager.setActualDefaultRingtoneUri(
S15.this, RingtoneManager.TYPE_RINGTONE,
newUri);
Toast.makeText(getApplicationContext(), "Ringtone set successfully",
Toast.LENGTH_SHORT).show();
So How do I do this? How do I define string for each file and how to pass them?
Since question is unclear for some members I will make it simpler
I don't have idea how should I write strings so when I start RingtoneManager Activity using Intent, I pass data from strings.
So How should I write my code to pass this
File name "Slow tone.mp3"
File path: R.raw.s8slowtone)
Ringtone name "Slow tone"
To call your activity, just place this function anywhere, and call it with your desired parameters. It will build an intent and fill in the parameters:
public static void runRingToneActivity(Context context, String ringToneName, String ringTonePath, String ringToneFilename) {
Intent intent=new Intent(context, RingToneActivity.class);
intent.putExtra("NAME", ringToneName);
intent.putExtra("PATH", ringTonePath);
intent.putExtra("FILE", ringToneFileName);
((Activity)context).startActivity(intent);
}
Inside your RingToneActivity's onCreate, you just retrieve the parameters you just passed:
#Override
protected void onCreate(Bundle savedInstanceState) {
.
.
Intent intent=this.getIntent();
String ringtoneName=intent.getStringExtra("NAME");
String ringtonePath=intent.getStringExtra("PATH");
String ringtoneFile=intent.getStringExtra("FILE");
// you have them, now use them!
}
NOTES:
Substitute "RingToneActivity.class" in the function for the name of your activity, if it is different.
Intent f27=new Intent(context, RMpro.class);
if (f27 != null){
f27.putExtra("FileName", "Horn!"); //Copied file name
int res = R.raw.s28horn; // Path to File in App ressources
f27.putExtra("FilePath", res); //Passing path with intent
f27.putExtra("RingName", "Horn.mp3"); // Ring name
((Activity)context).startActivity(f27);
}
And then in Ringtone Manager, in my case RMpro
final int FPATH=i.getExtras().getInt("FilePath");
final String RNAME = getIntent().getStringExtra("RingName").trim();
final String FNAME = getIntent().getStringExtra("FileName").trim();
And then just:
rsound = new File(rpath, FNAME);rpath.mkdirs();
InputStream in = getResources().openRawResource(FPATH);
values.put(MediaStore.MediaColumns.TITLE, RNAME);
You can pass entire objects via intent. You just need to implement Serializable interface for the class you want to pass. Example:
public class Ringtone implements Serializable
{
public String name;
public String path; //can be integer
public String file;
}
send via intent:
Ringtone ringtoneObj = new Ringtone();
intent.putExtra("test",ringtoneObj);
retrieve via intent:
Ringtone ringtoneFromIntent = (Ringtone) intent.getSerializableExtra("test");
Related
I would like to write to a file that I can access from the file system without being root.
This is my attempt:
FileOutputStream fos = null;
final String FILE_NAME = "test.txt";
fos = openFileOutput(FILE_NAME, MODE_PRIVATE);
fos.write("test".getBytes());
// Display path of file written to
Toast.makeText(this, "Saved to" + getFilesDir() + "/" + FILE_NAME, Toast.LENGTH_LONG).show();
Which writes to
/data/user/0/com.example.PROJECT_NAME/files/test.txt
which is not accessible without being root.
It would be good if there is a possibility to specify a different, absolute path which I know I can access, such as /data/data/....
My device is a Google Pixel C which unfortunately has no external SD-Card slot to write to.
After I figured out that the possibilities of accessing external storage differ quite a lot in different android versions, I chose to go with the Storage Access Framework (SAF). The SAF is an API (since API level 19), that offers the user a UI to browse through files.
Using an Intent, an UI pops up which lets the user create a file or choose an existing one:
private static final int CREATE_REQUEST_CODE = 40;
private Uri mFileLocation = null;
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/plain"); // specify file type
intent.putExtra(Intent.EXTRA_TITLE, "newfile.txt"); // default name for file
startActivityForResult(intent, CREATE_REQUEST_CODE);
After the user chose a file onActivityResult(...) gets called. Now it is possible to get the URI of the file by calling resultData.getData();
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode == Activity.RESULT_OK)
{
if (requestCode == CREATE_REQUEST_CODE)
{
if (resultData != null) {
mFileLocation = resultData.getData();
}
}
}
}
Now use this URI to write to the file:
private void writeFileContent(Uri uri, String contentToWrite)
{
try
{
ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(uri, "w"); // or 'wa' to append
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(contentToWrite.getBytes());
fileOutputStream.close();
pfd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I have a simple gallery app in which user can take or delete photos. For taking photos this works in notifying MediaStore of the newly created file:
File file = new File(storageDir, createImageName());
final Uri uri = Uri.fromFile(file);
Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(scanFileIntent);
I delete photos but local gallery app still shows them as a blank file.
This does not work. I target minimum Android 5.0 :
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "Folder where application stores photos");
final Uri uri = Uri.fromFile(file);
Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(scanFileIntent);
What I'm trying to do is to scan the folder my application creates when a file deleted to inform MediaStore of the images and folders deleted. How can I do this?
Here is a method that deletes any record(s) of a media file from the MediaStore.
Note that the DATA column in the MediaStore refers to the file's full path.
public static boolean deleteFileFromMediaStore(
Context context, String fileFullPath)
{
File file = new File(fileFullPath);
String absolutePath, canonicalPath;
try { absolutePath = file.getAbsolutePath(); }
catch (Exception ex) { absolutePath = null; }
try { canonicalPath = file.getCanonicalPath(); }
catch (Exception ex) { canonicalPath = null; }
ArrayList<String> paths = new ArrayList<>();
if (absolutePath != null) paths.add(absolutePath);
if (canonicalPath != null && !canonicalPath.equalsIgnoreCase(absolutePath))
paths.add(canonicalPath);
if (paths.size() == 0) return false;
ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Files.getContentUri("external");
boolean deleted = false;
for (String path : paths)
{
int result = resolver.delete(uri,
MediaStore.Files.FileColumns.DATA + "=?",
new String[] { path });
if (result != 0) deleted = true;
}
return deleted;
}
I'm currently developing an app and have the following issue.
While using NFC for device owner provisioning, I would like to send a string, which would be used by the new device owner app.
I'm aware of the standard MIME properties for device owner provisioning, found here
Here's a snippet that can give you a better visual of my issue. Notice the "myCustomValue" property.
Properties properties = new Properties();
properties.put("myCustomValue", value);
properties.put(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, "com.example.some.app");
try {
properties.store(stream, "NFC Provisioning");
ndefMessage = new NdefMessage(new NdefRecord[{NdefRecord.createMime(DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, stream.toByteArray())});
} catch (IOException e) {
}
This snippet lies inside
public NdefMessage createNdefMessage(NfcEvent event)
and you can find a template here
In case this is possible, I also would like to know how to retrieve that string value as soon as the provisioned app has started.
The code below should be what you're looking for. For brevity, I only set the package name plus two strings that will be sent to your DeviceAdminReceiver.
#Override
public NdefMessage createNdefMessage(NfcEvent event) {
try {
Properties p = new Properties();
p.setProperty(
DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
"com.example.some.app");
Properties extras = new Properties();
extras.setProperty("Key1", "TestString1");
extras.setProperty("Key2", "TestString2");
StringWriter sw = new StringWriter();
try{
extras.store(sw, "admin extras bundle");
p.put(DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
sw.toString());
Log.d(TAG, "Admin extras bundle=" + p.get(
DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE));
} catch (IOException e) {
Log.e(TAG, "Unable to build admin extras bundle");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStream out = new ObjectOutputStream(bos);
p.store(out, "");
final byte[] bytes = bos.toByteArray();
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, bytes));
return msg;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
This next snippet will go in your DeviceAdminReceiver in order to receive the "Admin Extras"... If you don't override onReceive, onProfileProvisioningComplete will need to be overridden with EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE handled in there instead.
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive " + intent.getAction());
if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(intent.getAction())) {
PersistableBundle extras = intent.getParcelableExtra(
EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
Log.d(TAG, "onReceive Extras:" + extras.getString("Key1") + " / " + extras.getString("Key2"));
}
}
onProfileProvisioningComplete will be overwritten like this.
#Override
public void onProfileProvisioningComplete(final Context context, final Intent intent)
{
final PersistableBundle extras = intent.getParcelableExtra(DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE);
Log.d(TAG, "onProfileProvisioningComplete Extras:" + extras.getString("Key1") + " / " + extras.getString("Key2"));
}
I referenced a question here about how one might approach (outside of Google Play) having an app essentially update itself. For testing, I simply wanted to try to see if I could get it to download and install. Unfortunately, I get a parse error.
I would greatly appreciate any help:
A snippet from the class that calls the AsyncTask class:
public class downloadReceiver extends BroadcastReceiver {
private Context context;
private long localUpdate;
private long remoteUpdate = 20;
#Override
public void onReceive(final Context c, Intent i) {
new Thread(new Runnable() {
#Override
public void run() {
SharedPreferences preferences = c.getSharedPreferences("config", c.MODE_PRIVATE);
final String store = preferences.getString("store", "");
final String id = preferences.getString("id", "");
final long lastUpdated = preferences.getLong("updated", 0);
// autoUpdate app
appUpdater updater = new appUpdater(c);
try {
updater.execute(new URL("http://midamcorp.com/myApp.php"));
} catch (Exception e) {Log.e(this.getClass().toString(), " " + e.getMessage()); }
and the appUpdater class:
public class appUpdater extends AsyncTask<URL, String, String> {
private Context c;
public appUpdater(Context context) {
this.c = context;
}
protected String doInBackground(URL... appUrl) {
String location = c.getFilesDir() + "/app.apk";
try {
URL url = appUrl[0];
URLConnection con = url.openConnection();
con.connect();
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(location);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.close();
input.close();
} catch(Exception e){
Log.e(this.getClass().toString(), " " + e.getMessage());
}
return location;
}
#Override
protected void onPostExecute(String saveLocation) {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
Log.i("Location of app is: ", " " + saveLocation);
i.setDataAndType(Uri.fromFile(new File(saveLocation)), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
c.startActivity(i);
}
}
Please note, the URL is linked to a PHP file that forces a download because the server I have it on has trouble with .apk files.
Your primary problem is that the installer does not have access to your portion of internal storage (getFilesDir()). Use external storage.
I also recommend that you call flush(), getFD().sync(), and close() in succession on your FileOutputStream, before trying to install the app.
I created a simple app to save a bitmap in internal storage and be able to show this bitmap in the android gallery like any other image.
The problem is: it isn't showing in the gallery after is saved... and I don't know what I am doing wrong. (I'm testing this on emulator)
Here is my class that handles the save of the Bitmap. This Saver class is used in my main activity when I click on a button saves the bitmap.
public class Saver {
private Context mContext;
private String mNameOfFolder = "Storager";
private String mNameOfFile = "Quote";
public void saveImage(Context context, Bitmap imageToSave) {
mContext = context;
// Create the directory
File dir = mContext.getDir(mNameOfFolder, Context.MODE_PRIVATE);
String filePath = dir.getAbsolutePath();
// Get the current date and time to attach to the name of the image
String currentDateAndTime = getCurrentDateAndTime();
if(!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, mNameOfFile + currentDateAndTime + ".jpg");
try {
FileOutputStream out = new FileOutputStream(file);
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
galleryAddPic(file);
successSave();
} catch (FileNotFoundException e) {
Log.d("d", "file not found");
unableToSave();
} catch (IOException e) {
Log.d("d", "IO Exception");
unableToSave();
}
}
private void galleryAddPic(File file) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
mContext.sendBroadcast(mediaScanIntent);
}
private String getCurrentDateAndTime() {
Calendar calendar = Calendar.getInstance();
// Setting format of the time
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
// Getting the formated date as a string
String formattedDate = dateFormat.format(calendar.getTime());
return formattedDate;
}
}
I even added the method galleryAddPic() that is provided by android developer site here to be able to add the image to Media Provider's database, making it available in the Android Gallery application and to other apps.
Third-party apps, including the MediaStore, have no access to your app's internal storage. If you want third-party apps to have access to the file, use external storage.
Also, I have no idea why you are creating a ContextWrapper here. If you have methods that require a Context (e.g., getDir() in your current code), call them on the Context that is passed into your method.